Skip to content

Commit 02784f3

Browse files
committed
[RFC] ANDROID: GPU frequency tracing support (002)
A periodic GPU frequency monitoring and tracing program for the Xe driver. The implementation provides bothperiodic sampling and event-driven frequency change notifications through the Linux ftrace infrastructure. Key features: - Periodic GPU frequency sampling with configurable intervals - Immediate frequency change reporting via tracepoints - Integration with Linux ftrace subsystem under 'power' events - Per-GT (Graphics Technology) monitoring support - Dedicated workqueue for non-blocking frequency sampling - Configurable via CONFIG_DRM_XE_GPUFREQTRACER kernel option - The monitoring interval can be configured at runtime via the sysfs (default 5sec). The sysfs entry is at: /sys/module/xe/parameters/gpufreq_monitoring_interval_ms The tracepoint is exposed at: /sys/kernel/debug/tracing/events/power/gpu_frequency Format: {unsigned int state, unsigned int gpu_id} - state: GPU frequency in KHz - gpu_id: GPU clock domain identifier This enables userspace tools and system monitoring applications to track GPU frequency changes for power management analysis, performance tuning, and debugging purposes. Bug: 388282937 Test: CtsGpuProfilingDataTestCases Change-Id: I7e8025004abd4b3ffdfb4d7214a4b4896be95a64 Signed-off-by: S Sebinraj <[email protected]>
1 parent 17c4bdc commit 02784f3

File tree

9 files changed

+502
-0
lines changed

9 files changed

+502
-0
lines changed

drivers/gpu/drm/xe/Kconfig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ config DRM_XE_FORCE_PROBE
8686

8787
Use "!*" to block the probe of the driver for all known devices.
8888

89+
config DRM_XE_GPUFREQTRACER
90+
bool "Enable XE GPU frequency tracing"
91+
depends on DRM_XE
92+
default n
93+
help
94+
Enable GPU frequency tracing support for Intel XE driver.
95+
This adds an ftrace tracepoint that reports GPU frequency changes
96+
at periodic boundaries (default 5 secs, configurable via the
97+
gpufreq_monitoring_interval_ms module parameter) and
98+
on direct frequency change events.
99+
100+
The monitoring interval can be configured at runtime via the sysfs module parameter:
101+
/sys/module/xe/parameters/gpufreq_monitoring_interval_ms
102+
103+
The tracepoint will be available at:
104+
/sys/kernel/debug/tracing/events/power/gpu_frequency
105+
106+
Format: {unsigned int state, unsigned int gpu_id}
107+
Where state is the frequency in KHz and gpu_id is the GPU clock domain.
108+
109+
If unsure, say N.
110+
89111
menu "drm/Xe Debugging"
90112
depends on DRM_XE
91113
depends on EXPERT

drivers/gpu/drm/xe/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ xe-$(CONFIG_PCI_IOV) += \
138138
xe_pci_sriov.o \
139139
xe_sriov_pf.o
140140

141+
# GPU frequency tracer
142+
xe-$(CONFIG_DRM_XE_GPUFREQTRACER) += xe_gpufreqtracer/xe_gpufreqtracer.o
143+
141144
# include helpers for tests even when XE is built-in
142145
ifdef CONFIG_DRM_XE_KUNIT_TEST
143146
xe-y += tests/xe_kunit_helpers.o

drivers/gpu/drm/xe/xe_device.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "xe_exec_queue.h"
3232
#include "xe_force_wake.h"
3333
#include "xe_ggtt.h"
34+
#include "xe_gpufreqtracer/xe_gpufreqtracer.h"
3435
#include "xe_gsc_proxy.h"
3536
#include "xe_gt.h"
3637
#include "xe_gt_mcr.h"
@@ -727,6 +728,21 @@ int xe_device_probe(struct xe_device *xe)
727728

728729
xe_heci_gsc_init(xe);
729730

731+
#ifdef CONFIG_DRM_XE_GPUFREQTRACER
732+
err = xe_gpufreqtracer_init(xe);
733+
if (err)
734+
goto err_fini_gt;
735+
736+
/* Start periodic monitoring on all GTs using global module parameter */
737+
for_each_gt(gt, xe, id) {
738+
err = xe_gpufreqtracer_start_monitoring(gt);
739+
if (err) {
740+
drm_err(&xe->drm, "xe_gpufreqtracer: failed to start monitoring for GT%u, err=%d\n",
741+
gt->info.id, err);
742+
}
743+
}
744+
#endif
745+
730746
err = xe_oa_init(xe);
731747
if (err)
732748
goto err_fini_gt;
@@ -788,6 +804,10 @@ void xe_device_remove(struct xe_device *xe)
788804

789805
xe_device_remove_display(xe);
790806

807+
#ifdef CONFIG_DRM_GPUFREQTRACER
808+
xe_gpufreqtracer_fini(xe);
809+
#endif
810+
791811
xe_display_fini(xe);
792812

793813
xe_oa_fini(xe);

drivers/gpu/drm/xe/xe_device_types.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#endif
3434

3535
struct xe_ggtt;
36+
struct xe_gpufreqtracer_data;
3637
struct xe_pat_ops;
3738

3839
#define XE_BO_INVALID_OFFSET LONG_MAX
@@ -496,6 +497,11 @@ struct xe_device {
496497
/** @oa: oa observation subsystem */
497498
struct xe_oa oa;
498499

500+
#ifdef CONFIG_DRM_XE_GPUFREQTRACER
501+
/** @gpufreqtracer_data: GPU frequency tracer data */
502+
struct xe_gpufreqtracer_data *gpufreqtracer_data;
503+
#endif
504+
499505
/** @needs_flr_on_fini: requests function-reset on fini */
500506
bool needs_flr_on_fini;
501507

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright © 2024 Intel Corporation
4+
*/
5+
6+
#include "xe_gpufreqtracer.h"
7+
8+
#include <linux/workqueue.h>
9+
#include <linux/timer.h>
10+
#include <linux/slab.h>
11+
#include <linux/jiffies.h>
12+
#include <linux/kernel.h>
13+
#include <linux/moduleparam.h>
14+
#include <linux/types.h>
15+
#include <linux/container_of.h>
16+
#include <linux/gfp.h>
17+
18+
#include "xe_device.h"
19+
#include "xe_gt.h"
20+
#include "xe_gt_types.h"
21+
#include "xe_guc_pc.h"
22+
#include "xe_module.h"
23+
24+
#define CREATE_TRACE_POINTS
25+
#include "xe_gpufreqtracer_trace.h"
26+
27+
/**
28+
* struct xe_gpufreqtracer_gt_data - Per-GT frequency monitoring data
29+
* @gt: Reference to the GT
30+
* @timer: Timer for periodic monitoring
31+
* @work: Work item for frequency sampling
32+
* @last_frequency: Last reported frequency to avoid duplicate reports
33+
* @monitoring_active: Whether monitoring is currently active
34+
*/
35+
struct xe_gpufreqtracer_gt_data {
36+
struct xe_gt *gt;
37+
struct timer_list timer;
38+
struct work_struct work;
39+
u32 last_frequency;
40+
bool monitoring_active;
41+
};
42+
43+
/**
44+
* struct xe_gpufreqtracer_data - Per-device frequency tracer data
45+
* @xe: Reference to the XE device
46+
* @gt_data: Array of per-GT monitoring data
47+
*/
48+
struct xe_gpufreqtracer_data {
49+
struct xe_device *xe;
50+
struct xe_gpufreqtracer_gt_data *gt_data;
51+
};
52+
53+
54+
/**
55+
* xe_gpufreqtracer_sample_work - Worker function to sample GPU frequency.
56+
* @work: Pointer to the work_struct representing the scheduled work.
57+
*
58+
* This function is executed in a workqueue context to periodically sample
59+
* the GPU frequency and perform any necessary tracing or logging operations.
60+
* It is part of the GPU frequency tracer subsystem.
61+
*/
62+
static void xe_gpufreqtracer_sample_work(struct work_struct *work)
63+
{
64+
struct xe_gpufreqtracer_gt_data *gt_data =
65+
container_of(work, struct xe_gpufreqtracer_gt_data, work);
66+
struct xe_gt *gt = gt_data->gt;
67+
struct xe_guc_pc *pc = &gt->uc.guc.pc;
68+
u32 current_freq;
69+
70+
if (!gt_data->monitoring_active) {
71+
drm_warn(&gt_to_xe(gt)->drm, "monitoring not active for GT%u, exiting",
72+
gt->info.id);
73+
return;
74+
}
75+
76+
current_freq = xe_guc_pc_get_act_freq(pc) * 1000; /* Convert MHz to KHz */
77+
78+
/* Only report if frequency has changed or this is the first sample */
79+
if (current_freq != gt_data->last_frequency) {
80+
drm_dbg(&gt_to_xe(gt)->drm, "GT%u frequency changed, tracing %u KHz",
81+
gt->info.id, current_freq);
82+
trace_gpu_frequency(current_freq, gt->info.id);
83+
gt_data->last_frequency = current_freq;
84+
}
85+
}
86+
87+
/**
88+
* xe_gpufreqtracer_timer_callback - Timer callback for GPU frequency tracer
89+
* @timer: Pointer to the timer_list structure associated with this callback
90+
*
91+
* This function is invoked when the timer associated with the GPU frequency tracer expires.
92+
* It is responsible for handling periodic tasks related to GPU frequency tracing, such as
93+
* sampling or logging frequency data.
94+
*/
95+
static void xe_gpufreqtracer_timer_callback(struct timer_list *timer)
96+
{
97+
struct xe_gpufreqtracer_gt_data *gt_data =
98+
container_of(timer, struct xe_gpufreqtracer_gt_data, timer);
99+
100+
if (gt_data->monitoring_active) {
101+
queue_work(system_highpri_wq, &gt_data->work);
102+
mod_timer(&gt_data->timer, jiffies +
103+
msecs_to_jiffies(xe_modparam.gpufreq_monitoring_interval_ms));
104+
} else {
105+
drm_warn(&gt_to_xe(gt_data->gt)->drm, "timer callback for GT%u but monitoring inactive",
106+
gt_data->gt->info.id);
107+
}
108+
}
109+
110+
/**
111+
* xe_gpufreqtracer_init - Initialize GPU frequency tracer for a device
112+
* @xe: The XE device
113+
*
114+
* Sets up the frequency tracer infrastructure for all GTs in the device.
115+
*
116+
* Return: 0 on success, negative error code on failure
117+
*/
118+
int xe_gpufreqtracer_init(struct xe_device *xe)
119+
{
120+
struct xe_gpufreqtracer_data *tracer_data;
121+
struct xe_gt *gt;
122+
u8 tile_id;
123+
int ret = 0;
124+
125+
tracer_data = kzalloc(sizeof(*tracer_data), GFP_KERNEL);
126+
if (!tracer_data)
127+
return -ENOMEM;
128+
129+
tracer_data->xe = xe;
130+
131+
/* Allocate GT data array based on actual GT count */
132+
tracer_data->gt_data = kcalloc(xe->info.gt_count,
133+
sizeof(*tracer_data->gt_data),
134+
GFP_KERNEL);
135+
if (!tracer_data->gt_data) {
136+
ret = -ENOMEM;
137+
goto err_free_tracer;
138+
}
139+
140+
/* Initialize per-GT data */
141+
for_each_gt(gt, xe, tile_id) {
142+
struct xe_gpufreqtracer_gt_data *gt_data =
143+
&tracer_data->gt_data[gt->info.id];
144+
145+
drm_dbg(&xe->drm, "initializing GT%u (tile %u)", gt->info.id, tile_id);
146+
147+
gt_data->gt = gt;
148+
gt_data->monitoring_active = false;
149+
gt_data->last_frequency = 0;
150+
151+
INIT_WORK(&gt_data->work, xe_gpufreqtracer_sample_work);
152+
timer_setup(&gt_data->timer, xe_gpufreqtracer_timer_callback, 0);
153+
154+
drm_dbg(&xe->drm, "GT%u initialized with global interval=%u ms",
155+
gt->info.id, xe_modparam.gpufreq_monitoring_interval_ms);
156+
}
157+
158+
xe->gpufreqtracer_data = tracer_data;
159+
return 0;
160+
161+
err_free_tracer:
162+
drm_err(&xe->drm, "initialization failed, freeing tracer data");
163+
kfree(tracer_data);
164+
return ret;
165+
}
166+
167+
/**
168+
* xe_gpufreqtracer_fini - Cleanup GPU frequency tracer for a device
169+
* @xe: The XE device
170+
*
171+
* Stops all monitoring and cleans up tracer resources.
172+
*/
173+
void xe_gpufreqtracer_fini(struct xe_device *xe)
174+
{
175+
struct xe_gpufreqtracer_data *tracer_data = xe->gpufreqtracer_data;
176+
struct xe_gt *gt;
177+
u8 tile_id;
178+
179+
if (!tracer_data) {
180+
drm_warn(&xe->drm, "no tracer data found, nothing to cleanup");
181+
return;
182+
}
183+
184+
/* Stop all monitoring */
185+
for_each_gt(gt, xe, tile_id) {
186+
drm_dbg(&xe->drm, "stopping monitoring for GT%u", gt->info.id);
187+
xe_gpufreqtracer_stop_monitoring(gt);
188+
}
189+
190+
kfree(tracer_data->gt_data);
191+
kfree(tracer_data);
192+
xe->gpufreqtracer_data = NULL;
193+
}
194+
195+
/**
196+
* xe_gpufreqtracer_report_frequency_change - Report frequency change directly
197+
* @gt: The GT instance
198+
* @frequency_khz: The new frequency in KHz
199+
*
200+
* Reports a frequency change immediately through the tracepoint.
201+
*/
202+
void xe_gpufreqtracer_report_frequency_change(struct xe_gt *gt, u32 frequency_khz)
203+
{
204+
drm_dbg(&gt_to_xe(gt)->drm, "direct frequency report for GT%u: %u KHz",
205+
gt->info.id, frequency_khz);
206+
207+
if (frequency_khz > 0) {
208+
trace_gpu_frequency(frequency_khz, gt->info.id);
209+
drm_dbg(&gt_to_xe(gt)->drm, "traced frequency change for GT%u", gt->info.id);
210+
}
211+
}
212+
213+
/**
214+
* xe_gpufreqtracer_start_monitoring - Start periodic frequency monitoring
215+
* @gt: The GT instance
216+
*
217+
* Starts periodic sampling of GPU frequency for the specified GT using the global
218+
* monitoring interval from module parameters.
219+
*
220+
* Return: 0 on success, negative error code on failure
221+
*/
222+
int xe_gpufreqtracer_start_monitoring(struct xe_gt *gt)
223+
{
224+
struct xe_gpufreqtracer_data *tracer_data = gt_to_xe(gt)->gpufreqtracer_data;
225+
struct xe_gpufreqtracer_gt_data *gt_data;
226+
227+
if (!tracer_data) {
228+
drm_warn(&gt_to_xe(gt)->drm, "no tracer data for GT%u, not supported", gt->info.id);
229+
return -EOPNOTSUPP;
230+
}
231+
232+
if (gt->info.id >= gt_to_xe(gt)->info.gt_count) {
233+
drm_err(&gt_to_xe(gt)->drm, "invalid GT ID %u, max supported is %u",
234+
gt->info.id, gt_to_xe(gt)->info.gt_count - 1);
235+
return -EINVAL;
236+
}
237+
238+
gt_data = &tracer_data->gt_data[gt->info.id];
239+
240+
if (gt_data->monitoring_active) {
241+
drm_warn(&gt_to_xe(gt)->drm, "monitoring already active for GT%u", gt->info.id);
242+
return -EALREADY;
243+
}
244+
245+
gt_data->monitoring_active = true;
246+
gt_data->last_frequency = 0;
247+
248+
/* Start the timer using global interval */
249+
mod_timer(&gt_data->timer, jiffies +
250+
msecs_to_jiffies(xe_modparam.gpufreq_monitoring_interval_ms));
251+
252+
drm_dbg(&gt_to_xe(gt)->drm, "monitoring started for GT%u with interval %u ms",
253+
gt->info.id, xe_modparam.gpufreq_monitoring_interval_ms);
254+
255+
return 0;
256+
}
257+
258+
/**
259+
* xe_gpufreqtracer_stop_monitoring - Stop periodic frequency monitoring
260+
* @gt: The GT instance
261+
*
262+
* Stops periodic sampling of GPU frequency for the specified GT.
263+
*/
264+
void xe_gpufreqtracer_stop_monitoring(struct xe_gt *gt)
265+
{
266+
struct xe_gpufreqtracer_data *tracer_data = gt_to_xe(gt)->gpufreqtracer_data;
267+
struct xe_gpufreqtracer_gt_data *gt_data;
268+
269+
if (!tracer_data || gt->info.id >= gt_to_xe(gt)->info.gt_count) {
270+
drm_err(&gt_to_xe(gt)->drm, "invalid tracer data or GT ID %u for stop request",
271+
gt->info.id);
272+
return;
273+
}
274+
275+
gt_data = &tracer_data->gt_data[gt->info.id];
276+
277+
if (!gt_data->monitoring_active) {
278+
drm_warn(&gt_to_xe(gt)->drm, "monitoring not active for GT%u, nothing to stop",
279+
gt->info.id);
280+
return;
281+
}
282+
283+
gt_data->monitoring_active = false;
284+
285+
del_timer_sync(&gt_data->timer);
286+
cancel_work_sync(&gt_data->work);
287+
}

0 commit comments

Comments
 (0)