GCC Code Coverage Report


Directory: ./
File: src/ppd-driver-intel-pstate.c
Date: 2024-09-13 00:56:02
Exec Total Coverage
Lines: 172 185 93.0%
Functions: 19 19 100.0%
Branches: 91 116 78.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2020 Bastien Nocera <hadess@hadess.net>
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 "CpuDriver"
11
12 #include <upower.h>
13
14 #include "ppd-utils.h"
15 #include "ppd-driver-intel-pstate.h"
16
17 #define CPU_DIR "/sys/devices/system/cpu/"
18 #define CPUFREQ_POLICY_DIR "/sys/devices/system/cpu/cpufreq/"
19 #define DEFAULT_CPU_FREQ_SCALING_GOV "powersave"
20 #define PSTATE_STATUS_PATH "/sys/devices/system/cpu/intel_pstate/status"
21 #define NO_TURBO_PATH "/sys/devices/system/cpu/intel_pstate/no_turbo"
22 #define TURBO_PCT_PATH "/sys/devices/system/cpu/intel_pstate/turbo_pct"
23
24 #define SYSTEMD_DBUS_NAME "org.freedesktop.login1"
25 #define SYSTEMD_DBUS_PATH "/org/freedesktop/login1"
26 #define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager"
27
28 struct _PpdDriverIntelPstate
29 {
30 PpdDriverCpu parent_instance;
31
32 PpdProfile activated_profile;
33 GPtrArray *epp_devices; /* Array of paths */
34 GPtrArray *epb_devices; /* Array of paths */
35 GFileMonitor *no_turbo_mon;
36 char *no_turbo_path;
37 gboolean on_battery;
38 };
39
40
4/5
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 130 times.
✓ Branch 3 taken 130 times.
✗ Branch 4 not taken.
788 G_DEFINE_TYPE (PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD_TYPE_DRIVER_CPU)
41
42 static gboolean ppd_driver_intel_pstate_activate_profile (PpdDriver *driver,
43 PpdProfile profile,
44 PpdProfileActivationReason reason,
45 GError **error);
46
47 static GObject*
48 134 ppd_driver_intel_pstate_constructor (GType type,
49 guint n_construct_params,
50 GObjectConstructParam *construct_params)
51 {
52 134 GObject *object;
53
54 134 object = G_OBJECT_CLASS (ppd_driver_intel_pstate_parent_class)->constructor (type,
55 n_construct_params,
56 construct_params);
57 134 g_object_set (object,
58 "driver-name", "intel_pstate",
59 "profiles", PPD_PROFILE_PERFORMANCE | PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER,
60 NULL);
61
62 134 return object;
63 }
64
65 static void
66 12 update_no_turbo (PpdDriverIntelPstate *pstate)
67 {
68 24 g_autofree char *contents = NULL;
69 12 gboolean turbo_disabled = FALSE;
70
71
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (g_file_get_contents (pstate->no_turbo_path, &contents, NULL, NULL)) {
72 12 contents = g_strchomp (contents);
73
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
12 if (g_strcmp0 (contents, "1") == 0)
74 turbo_disabled = TRUE;
75 }
76
77 12 g_object_set (G_OBJECT (pstate), "performance-degraded",
78 turbo_disabled ? "high-operating-temperature" : NULL,
79 NULL);
80 12 }
81
82 static void
83 12 no_turbo_changed (GFileMonitor *monitor,
84 GFile *file,
85 GFile *other_file,
86 GFileMonitorEvent event_type,
87 gpointer user_data)
88 {
89 12 PpdDriverIntelPstate *pstate = user_data;
90 12 g_autofree char *path = NULL;
91
92 12 path = g_file_get_path (file);
93 12 g_debug ("File monitor change happened for '%s' (event type %d)", path, event_type);
94
95
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
96
97
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
98 4 update_no_turbo (pstate);
99 }
100
101 static GFileMonitor *
102 8 monitor_no_turbo_prop (const char *path)
103 {
104 16 g_autoptr(GFile) no_turbo = NULL;
105
106
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
107 g_debug ("Not monitoring '%s' as it does not exist", path);
108 return NULL;
109 }
110
111 8 g_debug ("About to start monitoring '%s'", path);
112 8 no_turbo = g_file_new_for_path (path);
113 8 return g_file_monitor (no_turbo, G_FILE_MONITOR_NONE, NULL, NULL);
114 }
115
116 static gboolean
117 24 sys_has_turbo (void)
118 {
119 48 g_autofree char *turbo_pct_path = NULL;
120 24 g_autofree char *contents = NULL;
121 24 gboolean has_turbo = FALSE;
122
123 24 turbo_pct_path = ppd_utils_get_sysfs_path (TURBO_PCT_PATH);
124
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 8 times.
24 if (g_file_get_contents (turbo_pct_path, &contents, NULL, NULL)) {
125 16 contents = g_strchomp (contents);
126 16 has_turbo = (g_strcmp0 (contents, "0") != 0);
127 }
128
129 24 return has_turbo;
130 }
131
132 static gboolean
133 4 ppd_driver_intel_pstate_prepare_for_sleep (PpdDriver *driver,
134 gboolean start,
135 GError **error)
136 {
137 4 PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
138 8 g_autoptr(GError) local_error = NULL;
139
140
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (start)
141 return TRUE;
142
143 2 g_debug ("Re-applying energy_perf_bias");
144
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!ppd_driver_intel_pstate_activate_profile (PPD_DRIVER (pstate),
145 pstate->activated_profile,
146 PPD_PROFILE_ACTIVATION_REASON_RESUME,
147 &local_error)) {
148 g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
149 "Could not reapply energy_perf_bias preference on resume: ");
150 return FALSE;
151 }
152
153 return TRUE;
154 }
155
156 static PpdProbeResult
157 134 probe_epb (PpdDriverIntelPstate *pstate)
158 {
159 268 g_autoptr(GDir) dir = NULL;
160
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 86 times.
134 g_autofree char *policy_dir = NULL;
161 134 const char *dirname;
162
163 134 policy_dir = ppd_utils_get_sysfs_path (CPU_DIR);
164 134 dir = g_dir_open (policy_dir, 0, NULL);
165
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 48 times.
134 if (!dir) {
166 86 g_debug ("Could not open %s", CPU_DIR);
167 86 return PPD_PROBE_RESULT_FAIL;
168 }
169
170
2/2
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 48 times.
146 while ((dirname = g_dir_read_name (dir)) != NULL) {
171 98 g_autofree char *path = NULL;
172
173 98 path = g_build_filename (policy_dir,
174 dirname,
175 "power",
176 "energy_perf_bias",
177 NULL);
178
2/2
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 2 times.
98 if (!g_file_test (path, G_FILE_TEST_EXISTS))
179 96 continue;
180
181
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!pstate->epb_devices)
182 2 pstate->epb_devices = g_ptr_array_new_with_free_func (g_free);
183
184 2 g_ptr_array_add (pstate->epb_devices, g_steal_pointer (&path));
185 }
186
187
3/4
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
48 if (pstate->epb_devices && pstate->epb_devices->len)
188 return PPD_PROBE_RESULT_SUCCESS;
189
190 return PPD_PROBE_RESULT_FAIL;
191 }
192
193 static PpdProbeResult
194 134 probe_epp (PpdDriverIntelPstate *pstate)
195 {
196 268 g_autoptr(GDir) dir = NULL;
197
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 112 times.
134 g_autofree char *policy_dir = NULL;
198 134 g_autofree char *pstate_status_path = NULL;
199 134 g_autofree char *status = NULL;
200 134 const char *dirname;
201
202 /* Verify that Intel P-State is running in active mode */
203 134 pstate_status_path = ppd_utils_get_sysfs_path (PSTATE_STATUS_PATH);
204
2/2
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 26 times.
134 if (!g_file_get_contents (pstate_status_path, &status, NULL, NULL))
205 return PPD_PROBE_RESULT_FAIL;
206 26 status = g_strchomp (status);
207
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 22 times.
26 if (g_strcmp0 (status, "active") != 0) {
208 4 g_debug ("Intel P-State is running in passive mode");
209 4 return PPD_PROBE_RESULT_FAIL;
210 }
211
212 22 policy_dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR);
213 22 dir = g_dir_open (policy_dir, 0, NULL);
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (!dir) {
215 g_debug ("Could not open %s", policy_dir);
216 return PPD_PROBE_RESULT_FAIL;
217 }
218
219
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 22 times.
48 while ((dirname = g_dir_read_name (dir)) != NULL) {
220 26 g_autofree char *path = NULL;
221 26 g_autofree char *gov_path = NULL;
222 26 g_autoptr(GError) error = NULL;
223
224 26 path = g_build_filename (policy_dir,
225 dirname,
226 "energy_performance_preference",
227 NULL);
228
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if (!g_file_test (path, G_FILE_TEST_EXISTS))
229 continue;
230
231 /* Force a scaling_governor where the preference can be written */
232 26 gov_path = g_build_filename (policy_dir,
233 dirname,
234 "scaling_governor",
235 NULL);
236
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if (!ppd_utils_write (gov_path, DEFAULT_CPU_FREQ_SCALING_GOV, &error)) {
237 g_warning ("Could not change scaling governor %s to '%s'", dirname, DEFAULT_CPU_FREQ_SCALING_GOV);
238 continue;
239 }
240
241
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 4 times.
26 if (!pstate->epp_devices)
242 22 pstate->epp_devices = g_ptr_array_new_with_free_func (g_free);
243
244
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 g_ptr_array_add (pstate->epp_devices, g_steal_pointer (&path));
245 }
246
247
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 if (pstate->epp_devices && pstate->epp_devices->len)
248 22 return PPD_PROBE_RESULT_SUCCESS;
249
250 return PPD_PROBE_RESULT_FAIL;
251 }
252
253 static PpdProbeResult
254 134 ppd_driver_intel_pstate_probe (PpdDriver *driver)
255 {
256 134 PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
257 134 PpdProbeResult ret = PPD_PROBE_RESULT_FAIL;
258 134 PpdProbeResult epp_ret, epb_ret;
259 134 gboolean has_turbo;
260
261 134 epp_ret = probe_epp (pstate);
262 134 epb_ret = probe_epb (pstate);
263
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 22 times.
134 ret = (epp_ret == PPD_PROBE_RESULT_SUCCESS) ? epp_ret : epb_ret;
264
265
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 2 times.
112 if (ret != PPD_PROBE_RESULT_SUCCESS)
266 110 goto out;
267
268 24 has_turbo = sys_has_turbo ();
269
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
24 if (has_turbo) {
270 /* Monitor the first "no_turbo" */
271 8 pstate->no_turbo_path = ppd_utils_get_sysfs_path (NO_TURBO_PATH);
272 8 pstate->no_turbo_mon = monitor_no_turbo_prop (pstate->no_turbo_path);
273
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (pstate->no_turbo_mon) {
274 8 g_signal_connect_object (G_OBJECT (pstate->no_turbo_mon), "changed",
275 G_CALLBACK (no_turbo_changed), pstate, 0);
276 }
277 8 update_no_turbo (pstate);
278 }
279
280 16 out:
281 134 g_debug ("%s Intel p-state settings",
282 ret == PPD_PROBE_RESULT_SUCCESS ? "Found" : "Didn't find");
283
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 110 times.
134 if (ret == PPD_PROBE_RESULT_SUCCESS) {
284
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 22 times.
26 g_debug ("\tEnergy Performance Preference: %s",
285 epp_ret == PPD_PROBE_RESULT_SUCCESS ? "yes" : "no");
286 24 g_debug ("\tEnergy Performance Bias: %s",
287 epp_ret == PPD_PROBE_RESULT_SUCCESS ? "yes" : "no");
288
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
40 g_debug ("\tHas Turbo: %s", has_turbo ? "yes" : "no");
289 }
290 134 return ret;
291 }
292
293 static const char *
294 48 profile_to_epp_pref (PpdProfile profile, gboolean battery)
295 {
296 /* Note that we don't check "energy_performance_available_preferences"
297 * as all the values are always available */
298
2/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
48 switch (profile) {
299 case PPD_PROFILE_POWER_SAVER:
300 return "power";
301 42 case PPD_PROFILE_BALANCED:
302
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 6 times.
42 return battery ? "balance_power" : "balance_performance";
303 6 case PPD_PROFILE_PERFORMANCE:
304 6 return "performance";
305 }
306
307 g_return_val_if_reached (NULL);
308 }
309
310 static const char *
311 6 profile_to_epb_pref (PpdProfile profile, gboolean battery)
312 {
313 /* From arch/x86/include/asm/msr-index.h
314 * See ENERGY_PERF_BIAS_* */
315
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
6 switch (profile) {
316 case PPD_PROFILE_POWER_SAVER:
317 return "15";
318 2 case PPD_PROFILE_BALANCED:
319
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 return battery ? "8" : "6";
320 2 case PPD_PROFILE_PERFORMANCE:
321 2 return "0";
322 }
323
324 g_return_val_if_reached (NULL);
325 }
326
327 static gboolean
328 54 apply_pref_to_devices (PpdDriver *driver,
329 PpdProfile profile,
330 GError **error)
331 {
332 54 PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
333
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (profile == PPD_PROFILE_UNSET)
335 return TRUE;
336
337
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
54 g_return_val_if_fail (pstate->epp_devices != NULL ||
338 pstate->epb_devices != NULL, FALSE);
339
5/8
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
54 g_return_val_if_fail ((pstate->epp_devices && pstate->epp_devices->len != 0) ||
340 (pstate->epb_devices && pstate->epb_devices->len != 0), FALSE);
341
342
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 6 times.
54 if (pstate->epp_devices) {
343 48 const char *epp_pref = profile_to_epp_pref (profile, pstate->on_battery);
344
345
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 44 times.
48 if (!ppd_utils_write_files (pstate->epp_devices, epp_pref, error))
346 return FALSE;
347 }
348
349
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 44 times.
50 if (pstate->epb_devices) {
350 6 const char *epb_pref = profile_to_epb_pref (profile, pstate->on_battery);
351
352
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!ppd_utils_write_files (pstate->epb_devices, epb_pref, error))
353 return FALSE;
354 }
355
356 50 pstate->activated_profile = profile;
357
358 50 return TRUE;
359 }
360
361 static gboolean
362 18 ppd_driver_intel_pstate_power_changed (PpdDriver *driver,
363 PpdPowerChangedReason reason,
364 GError **error)
365 {
366 18 PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
367
368
2/3
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
18 switch (reason) {
369 12 case PPD_POWER_CHANGED_REASON_UNKNOWN:
370 case PPD_POWER_CHANGED_REASON_AC:
371 12 pstate->on_battery = FALSE;
372 12 break;
373 6 case PPD_POWER_CHANGED_REASON_BATTERY:
374 6 pstate->on_battery = TRUE;
375 6 break;
376 default:
377 g_return_val_if_reached (FALSE);
378 }
379
380 18 return apply_pref_to_devices (driver,
381 pstate->activated_profile,
382 error);
383 }
384
385 static gboolean
386 36 ppd_driver_intel_pstate_activate_profile (PpdDriver *driver,
387 PpdProfile profile,
388 PpdProfileActivationReason reason,
389 GError **error)
390 {
391 36 return apply_pref_to_devices (driver, profile, error);
392 }
393
394 static void
395 134 ppd_driver_intel_pstate_finalize (GObject *object)
396 {
397 134 PpdDriverIntelPstate *driver;
398
399 134 driver = PPD_DRIVER_INTEL_PSTATE (object);
400
401
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 112 times.
134 g_clear_pointer (&driver->epp_devices, g_ptr_array_unref);
402
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 132 times.
134 g_clear_pointer (&driver->epb_devices, g_ptr_array_unref);
403
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 126 times.
134 g_clear_pointer (&driver->no_turbo_path, g_free);
404
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 126 times.
134 g_clear_object (&driver->no_turbo_mon);
405 134 G_OBJECT_CLASS (ppd_driver_intel_pstate_parent_class)->finalize (object);
406 134 }
407
408 static void
409 130 ppd_driver_intel_pstate_class_init (PpdDriverIntelPstateClass *klass)
410 {
411 130 GObjectClass *object_class;
412 130 PpdDriverClass *driver_class;
413
414 130 object_class = G_OBJECT_CLASS (klass);
415 130 object_class->constructor = ppd_driver_intel_pstate_constructor;
416 130 object_class->finalize = ppd_driver_intel_pstate_finalize;
417
418 130 driver_class = PPD_DRIVER_CLASS (klass);
419 130 driver_class->probe = ppd_driver_intel_pstate_probe;
420 130 driver_class->activate_profile = ppd_driver_intel_pstate_activate_profile;
421 130 driver_class->prepare_to_sleep = ppd_driver_intel_pstate_prepare_for_sleep;
422 130 driver_class->power_changed = ppd_driver_intel_pstate_power_changed;
423 }
424
425 static void
426 134 ppd_driver_intel_pstate_init (PpdDriverIntelPstate *self)
427 {
428 134 }
429