GCC Code Coverage Report


Directory: ./
File: src/ppd-action-amdgpu-dpm.c
Date: 2026-07-20 20:05:00
Exec Total Coverage
Lines: 62 70 88.6%
Functions: 10 10 100.0%
Branches: 19 28 67.9%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2024 Advanced Micro Devices
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3 as published by
6 * the Free Software Foundation.
7 *
8 */
9
10 #define G_LOG_DOMAIN "AmdgpuDpm"
11
12 #include "config.h"
13
14 #include <gudev/gudev.h>
15
16 #include "ppd-action-amdgpu-dpm.h"
17 #include "ppd-profile.h"
18 #include "ppd-utils.h"
19
20 #define DPM_SYSFS_NAME "device/power_dpm_force_performance_level"
21
22 /**
23 * SECTION:ppd-action-amdgpu-dpm
24 * @Short_description: Power savings for GPU clocks
25 * @Title: AMDGPU DPM clock control
26 *
27 * The AMDGPU DPM clock control action utilizes the sysfs attribute present on some DRM
28 * connectors for amdgpu called "power_dpm_force_performance_level".
29 */
30
31 struct _PpdActionAmdgpuDpm
32 {
33 PpdAction parent_instance;
34 PpdProfile last_profile;
35
36 GUdevClient *client;
37 };
38
39
4/5
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 142 times.
✗ Branch 4 not taken.
884 G_DEFINE_TYPE (PpdActionAmdgpuDpm, ppd_action_amdgpu_dpm, PPD_TYPE_ACTION)
40
41 static GObject*
42 158 ppd_action_amdgpu_dpm_constructor (GType type,
43 guint n_construct_params,
44 GObjectConstructParam *construct_params)
45 {
46 158 GObject *object;
47
48 158 object = G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->constructor (type,
49 n_construct_params,
50 construct_params);
51 158 g_object_set (object,
52 "action-name", "amdgpu_dpm",
53 "action-description", "Adjust GPU dynamic power management",
54 "action-optin", TRUE,
55 NULL);
56
57 158 return object;
58 }
59
60 static gboolean
61 4 ppd_action_amdgpu_dpm_update_target (PpdActionAmdgpuDpm *self, GError **error)
62 {
63 8 g_autolist (GUdevDevice) devices = NULL;
64 4 const gchar *target;
65
66
2/3
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
4 switch (self->last_profile) {
67 case PPD_PROFILE_POWER_SAVER:
68 target = "low";
69 break;
70 2 case PPD_PROFILE_BALANCED:
71 case PPD_PROFILE_PERFORMANCE:
72 2 target = "auto";
73 2 break;
74 ✗ default:
75 ✗ g_message ("No profile set, skipping amdgpu dpm update");
76 ✗ return FALSE;
77 }
78
79 4 devices = g_udev_client_query_by_subsystem (self->client, "drm");
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (devices == NULL) {
81 ✗ g_set_error_literal (error,
82 G_IO_ERROR,
83 G_IO_ERROR_NOT_FOUND,
84 "no drm devices found");
85 ✗ return FALSE;
86 }
87
88
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (GList *l = devices; l != NULL; l = l->next) {
89 4 GUdevDevice *dev = l->data;
90 4 const char *value;
91
92 4 value = g_udev_device_get_devtype (dev);
93
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (g_strcmp0 (value, "drm_minor") != 0)
94 ✗ continue;
95
96 4 value = g_udev_device_get_sysfs_attr_uncached (dev, DPM_SYSFS_NAME);
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!value)
98 ✗ continue;
99
100
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (g_strcmp0 (value, target) == 0) {
101 1 g_info ("Device %s already set to %s", g_udev_device_get_sysfs_path (dev), target);
102 1 continue;
103 }
104
105
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if (g_strcmp0 (value, "manual") == 0) {
106 2 g_info ("Device %s is in manual mode, not changing", g_udev_device_get_sysfs_path (dev));
107 2 continue;
108 }
109
110 1 g_info ("Setting device %s to %s", g_udev_device_get_sysfs_path (dev), target);
111
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!ppd_utils_write_sysfs (dev, DPM_SYSFS_NAME, target, error))
112 return FALSE;
113 }
114
115 return TRUE;
116 }
117
118 static gboolean
119 4 ppd_action_amdgpu_dpm_activate_profile (PpdAction *action,
120 PpdProfile profile,
121 GError **error)
122 {
123 4 PpdActionAmdgpuDpm *self = PPD_ACTION_AMDGPU_DPM (action);
124 4 self->last_profile = profile;
125
126 4 return ppd_action_amdgpu_dpm_update_target (self, error);
127 }
128
129 static void
130 2 udev_uevent_cb (GUdevClient *client,
131 gchar *action,
132 GUdevDevice *device,
133 gpointer user_data)
134 {
135 2 PpdActionAmdgpuDpm *self = user_data;
136
137 2 g_debug ("Device %s %s", g_udev_device_get_sysfs_path (device), action);
138
139
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!g_str_equal (action, "add"))
140 return;
141
142
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!g_udev_device_has_sysfs_attr (device, DPM_SYSFS_NAME))
143 return;
144
145 ✗ ppd_action_amdgpu_dpm_update_target (self, NULL);
146 }
147
148 static PpdProbeResult
149 2 ppd_action_amdgpu_dpm_probe (PpdAction *action)
150 {
151 2 return ppd_utils_match_cpu_vendor ("AuthenticAMD") ?
152 2 PPD_PROBE_RESULT_SUCCESS : PPD_PROBE_RESULT_FAIL;
153 }
154
155 static void
156 158 ppd_action_amdgpu_dpm_finalize (GObject *object)
157 {
158 158 PpdActionAmdgpuDpm *action;
159
160 158 action = PPD_ACTION_AMDGPU_DPM (object);
161
1/2
✓ Branch 0 taken 158 times.
✗ Branch 1 not taken.
158 g_clear_object (&action->client);
162 158 G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->finalize (object);
163 158 }
164
165 static void
166 142 ppd_action_amdgpu_dpm_class_init (PpdActionAmdgpuDpmClass *klass)
167 {
168 142 GObjectClass *object_class;
169 142 PpdActionClass *driver_class;
170
171 142 object_class = G_OBJECT_CLASS(klass);
172 142 object_class->constructor = ppd_action_amdgpu_dpm_constructor;
173 142 object_class->finalize = ppd_action_amdgpu_dpm_finalize;
174
175 142 driver_class = PPD_ACTION_CLASS(klass);
176 142 driver_class->probe = ppd_action_amdgpu_dpm_probe;
177 142 driver_class->activate_profile = ppd_action_amdgpu_dpm_activate_profile;
178 }
179
180 static void
181 158 ppd_action_amdgpu_dpm_init (PpdActionAmdgpuDpm *self)
182 {
183 158 const gchar * const subsystem[] = { "drm", NULL };
184
185 158 self->client = g_udev_client_new (subsystem);
186 158 g_signal_connect_object (G_OBJECT (self->client), "uevent",
187 G_CALLBACK (udev_uevent_cb), self, 0);
188 158 }
189