Skip to content

Commit f0a569a

Browse files
authored
Merge pull request #548 from cjwilsontech/x3d-vcache-optimizer-support
Support setting the X3D V-Cache Mode Adds support for adjusting the AMD X3D V-Cache mode(https://www.phoronix.com/news/AMD-3DV-Cache-Optimizer-Linux) for systems with the latest optimizer driver support using `amd_x3d_mode` in the Linux driver for dual-CCD systems. This allows GameMode to adjust the system's preference for which CCD to schedule tasks on, opening opportunities for optimizing a system in new ways. For example, if a system is normally in `cache` mode to optimize for cache-sensitive tasks, this setting can be used to shift those to the `frequency` CCD temporarily while GameMode is running a game process pinned on the cache CCD, and then switch it back afterwards. Changes: - Adds two new config items, `amd_x3d_mode_desired` and `amd_x3d_mode_default` that can be set to either `frequency` or `cache`. - Adds a new utility, `x3dmodectl` for getting or updating the X3D mode. - Includes the new utility in the test command.
2 parents aac2cf7 + f86084a commit f0a569a

File tree

9 files changed

+465
-3
lines changed

9 files changed

+465
-3
lines changed

daemon/gamemode-config.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ struct GameModeConfig {
115115

116116
char cpu_park_cores[CONFIG_VALUE_MAX];
117117
char cpu_pin_cores[CONFIG_VALUE_MAX];
118+
char amd_x3d_mode_desired[CONFIG_VALUE_MAX];
119+
char amd_x3d_mode_default[CONFIG_VALUE_MAX];
118120

119121
long require_supervisor;
120122
char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
@@ -243,6 +245,23 @@ static bool get_string_value(const char *value, char output[CONFIG_VALUE_MAX])
243245
return true;
244246
}
245247

248+
/*
249+
* Get and validate an x3d mode value
250+
*/
251+
static bool get_x3d_mode_value(const char *name, const char *value, char output[CONFIG_VALUE_MAX])
252+
{
253+
if (strcmp(value, "frequency") != 0 && strcmp(value, "cache") != 0) {
254+
LOG_ERROR("Config: %s has invalid value '%s'. Valid values are 'frequency' or 'cache'\n",
255+
name,
256+
value);
257+
return false;
258+
}
259+
260+
strncpy(output, value, CONFIG_VALUE_MAX - 1);
261+
output[CONFIG_VALUE_MAX - 1] = '\0';
262+
return true;
263+
}
264+
246265
/* Controls whether to read the protected config variables */
247266
static bool load_protected = false;
248267

@@ -319,6 +338,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
319338
valid = get_string_value(value, self->values.cpu_park_cores);
320339
} else if (strcmp(name, "pin_cores") == 0) {
321340
valid = get_string_value(value, self->values.cpu_pin_cores);
341+
} else if (strcmp(name, "amd_x3d_mode_desired") == 0) {
342+
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_desired);
343+
} else if (strcmp(name, "amd_x3d_mode_default") == 0) {
344+
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_default);
322345
}
323346
} else if (strcmp(section, "supervisor") == 0) {
324347
/* Supervisor subsection */
@@ -866,6 +889,22 @@ void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]
866889
sizeof(self->values.cpu_pin_cores));
867890
}
868891

892+
void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
893+
{
894+
memcpy_locked_config(self,
895+
value,
896+
&self->values.amd_x3d_mode_desired,
897+
sizeof(self->values.amd_x3d_mode_desired));
898+
}
899+
900+
void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
901+
{
902+
memcpy_locked_config(self,
903+
value,
904+
&self->values.amd_x3d_mode_default,
905+
sizeof(self->values.amd_x3d_mode_default));
906+
}
907+
869908
/*
870909
* Checks if the supervisor is whitelisted
871910
*/

daemon/gamemode-config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VA
127127
*/
128128
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
129129
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
130+
void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
131+
void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
130132

131133
/**
132134
* Functions to get supervisor config permissions

daemon/gamemode-context.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ struct GameModeContext {
105105

106106
long initial_split_lock_mitigate;
107107

108+
char initial_x3d_mode[64]; /**<Initial x3d mode to restore */
109+
108110
/* Reaper control */
109111
struct {
110112
pthread_t thread;
@@ -168,6 +170,9 @@ void game_mode_context_init(GameModeContext *self)
168170

169171
self->initial_split_lock_mitigate = -1;
170172

173+
/* clear the initial x3d mode string */
174+
memset(self->initial_x3d_mode, 0, sizeof(self->initial_x3d_mode));
175+
171176
pthread_rwlock_init(&self->rwlock, NULL);
172177

173178
/* Get the reaper thread going */
@@ -256,6 +261,89 @@ static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
256261
return 0;
257262
}
258263

264+
static void game_mode_store_x3d_mode(GameModeContext *self)
265+
{
266+
char x3d_mode_desired[CONFIG_VALUE_MAX] = { 0 };
267+
config_get_amd_x3d_mode_desired(self->config, x3d_mode_desired);
268+
if (x3d_mode_desired[0] == '\0') {
269+
return;
270+
}
271+
272+
if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
273+
LOG_MSG("x3dmodectl utility not found, X3D mode control disabled\n");
274+
return;
275+
}
276+
277+
const char *const exec_args[] = {
278+
LIBEXECDIR "/x3dmodectl",
279+
"get",
280+
NULL,
281+
};
282+
283+
char output[EXTERNAL_BUFFER_MAX] = { 0 };
284+
int ret = run_external_process(exec_args, output, -1);
285+
if (ret != 0) {
286+
LOG_MSG("X3D mode hardware not available or failed to get current mode\n");
287+
return;
288+
}
289+
290+
strncpy(self->initial_x3d_mode, output, sizeof(self->initial_x3d_mode) - 1);
291+
self->initial_x3d_mode[sizeof(self->initial_x3d_mode) - 1] = '\0';
292+
char *newline = strchr(self->initial_x3d_mode, '\n');
293+
if (newline) {
294+
*newline = '\0';
295+
}
296+
297+
LOG_MSG("x3d mode was initially set to [%s]\n", self->initial_x3d_mode);
298+
}
299+
300+
static int game_mode_set_x3d_mode(GameModeContext *self, bool desired)
301+
{
302+
char x3d_mode_config[CONFIG_VALUE_MAX] = { 0 };
303+
304+
if (desired) {
305+
config_get_amd_x3d_mode_desired(self->config, x3d_mode_config);
306+
} else {
307+
config_get_amd_x3d_mode_default(self->config, x3d_mode_config);
308+
if (x3d_mode_config[0] == '\0') {
309+
if (self->initial_x3d_mode[0] != '\0') {
310+
strncpy(x3d_mode_config, self->initial_x3d_mode, CONFIG_VALUE_MAX - 1);
311+
x3d_mode_config[CONFIG_VALUE_MAX - 1] = '\0';
312+
} else {
313+
return 0;
314+
}
315+
}
316+
}
317+
318+
if (x3d_mode_config[0] == '\0') {
319+
return 0;
320+
}
321+
322+
if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
323+
LOG_MSG("x3dmodectl utility not found, skipping X3D mode change\n");
324+
return 0;
325+
}
326+
327+
if (strcmp(x3d_mode_config, "frequency") != 0 && strcmp(x3d_mode_config, "cache") != 0) {
328+
LOG_ERROR("Invalid X3D mode '%s'. Valid modes are 'frequency' or 'cache'\n",
329+
x3d_mode_config);
330+
return -1;
331+
}
332+
333+
const char *const exec_args[] = {
334+
"pkexec", LIBEXECDIR "/x3dmodectl", "set", x3d_mode_config, NULL,
335+
};
336+
337+
LOG_MSG("Requesting update of X3D mode to %s\n", x3d_mode_config);
338+
int ret = run_external_process(exec_args, NULL, -1);
339+
if (ret != 0) {
340+
LOG_ERROR("Failed to update X3D mode\n");
341+
return ret;
342+
}
343+
344+
return 0;
345+
}
346+
259347
static void game_mode_store_governor(GameModeContext *self)
260348
{
261349
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
@@ -468,6 +556,8 @@ static void game_mode_context_store_defaults(GameModeContext *self)
468556
game_mode_store_governor(self);
469557

470558
game_mode_store_splitlock(self);
559+
560+
game_mode_store_x3d_mode(self);
471561
}
472562

473563
/**
@@ -504,6 +594,8 @@ static void game_mode_context_enter(GameModeContext *self)
504594

505595
game_mode_disable_splitlock(self, true);
506596

597+
game_mode_set_x3d_mode(self, true);
598+
507599
/* Apply GPU optimisations by first getting the current values, and then setting the target */
508600
game_mode_get_gpu(self->stored_gpu);
509601
game_mode_apply_gpu(self->target_gpu);
@@ -548,6 +640,8 @@ static void game_mode_context_leave(GameModeContext *self)
548640

549641
game_mode_disable_splitlock(self, false);
550642

643+
game_mode_set_x3d_mode(self, false);
644+
551645
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);
552646

553647
game_mode_disable_igpu_optimization(self);

daemon/gamemode-tests.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ POSSIBILITY OF SUCH DAMAGE.
4242
#include "gamemode-config.h"
4343
#include "gamemode_client.h"
4444

45+
#include "build-config.h"
46+
4547
#include <pthread.h>
4648
#include <sys/syscall.h>
4749
#include <sys/wait.h>
@@ -837,6 +839,104 @@ int run_ioprio_tests(struct GameModeConfig *config)
837839
return ret;
838840
}
839841

842+
/* Check the AMD X3D mode setting works */
843+
static int run_x3d_mode_tests(struct GameModeConfig *config)
844+
{
845+
/* Get the two config parameters we care about */
846+
char desired_mode[CONFIG_VALUE_MAX] = { 0 };
847+
config_get_amd_x3d_mode_desired(config, desired_mode);
848+
849+
if (desired_mode[0] == '\0') {
850+
/* Not configured */
851+
return 1;
852+
}
853+
854+
char default_mode[CONFIG_VALUE_MAX] = { 0 };
855+
config_get_amd_x3d_mode_default(config, default_mode);
856+
857+
/* Get the initial X3D mode state */
858+
char initial_mode[64] = { 0 };
859+
const char *const get_args[] = {
860+
LIBEXECDIR "/x3dmodectl",
861+
"get",
862+
NULL,
863+
};
864+
865+
char output[EXTERNAL_BUFFER_MAX] = { 0 };
866+
int ret = run_external_process(get_args, output, -1);
867+
if (ret != 0) {
868+
return 1;
869+
}
870+
871+
/* Store the initial mode, removing any trailing newline */
872+
strncpy(initial_mode, output, sizeof(initial_mode) - 1);
873+
initial_mode[sizeof(initial_mode) - 1] = '\0';
874+
char *newline = strchr(initial_mode, '\n');
875+
if (newline) {
876+
*newline = '\0';
877+
}
878+
879+
/* Check if hardware is available */
880+
if (strcmp(initial_mode, "unavailable") == 0) {
881+
return 1;
882+
}
883+
884+
/* Start gamemode */
885+
gamemode_request_start();
886+
887+
/* Give gamemode time to apply settings */
888+
usleep(500000);
889+
890+
/* Verify the mode is the desired one */
891+
ret = run_external_process(get_args, output, -1);
892+
if (ret != 0) {
893+
LOG_ERROR("Failed to get X3D mode after gamemode start\n");
894+
gamemode_request_end();
895+
return -1;
896+
}
897+
898+
/* Remove trailing newline from output */
899+
newline = strchr(output, '\n');
900+
if (newline) {
901+
*newline = '\0';
902+
}
903+
904+
if (strcmp(output, desired_mode) != 0) {
905+
LOG_ERROR("X3D mode was not set to %s (was actually %s)!\n", desired_mode, output);
906+
gamemode_request_end();
907+
return -1;
908+
}
909+
910+
/* End gamemode */
911+
gamemode_request_end();
912+
913+
/* Give gamemode time to restore settings */
914+
usleep(500000);
915+
916+
/* Verify the mode is restored */
917+
ret = run_external_process(get_args, output, -1);
918+
if (ret != 0) {
919+
LOG_ERROR("Failed to get X3D mode after gamemode end\n");
920+
return -1;
921+
}
922+
923+
/* Remove trailing newline from output */
924+
newline = strchr(output, '\n');
925+
if (newline) {
926+
*newline = '\0';
927+
}
928+
929+
/* Determine expected restored mode */
930+
const char *expected_mode = (default_mode[0] != '\0') ? default_mode : initial_mode;
931+
932+
if (strcmp(output, expected_mode) != 0) {
933+
LOG_ERROR("X3D mode was not restored to %s (was actually %s)!\n", expected_mode, output);
934+
return -1;
935+
}
936+
937+
return 0;
938+
}
939+
840940
/**
841941
* game_mode_run_feature_tests runs a set of tests for each current feature (based on the current
842942
* config) returns 0 for success, -1 for failure
@@ -947,6 +1047,21 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
9471047
}
9481048
}
9491049

1050+
/* Was the AMD X3D mode changed? */
1051+
{
1052+
LOG_MSG("::: Verifying AMD X3D mode\n");
1053+
int x3dstatus = run_x3d_mode_tests(config);
1054+
1055+
if (x3dstatus == 1)
1056+
LOG_MSG("::: Passed (AMD X3D mode not configured)\n");
1057+
else if (x3dstatus == 0)
1058+
LOG_MSG("::: Passed\n");
1059+
else {
1060+
LOG_MSG("::: Failed!\n");
1061+
status = -1;
1062+
}
1063+
}
1064+
9501065
/* TODO */
9511066
/* Was the scheduling applied and removed? Does it get applied to a full process tree? */
9521067
/* Does the screensaver get inhibited? Unknown if this is testable, org.freedesktop.ScreenSaver

data/polkit/actions/com.feralinteractive.GameMode.policy.in

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,16 @@
7070
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/platprofctl</annotate>
7171
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
7272
</action>
73+
74+
<action id="com.feralinteractive.GameMode.x3dmode-helper">
75+
<description>Modify the AMD X3D cache mode</description>
76+
<message>Authentication is required to modify AMD X3D cache mode</message>
77+
<defaults>
78+
<allow_any>no</allow_any>
79+
<allow_inactive>no</allow_inactive>
80+
<allow_active>no</allow_active>
81+
</defaults>
82+
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/x3dmodectl</annotate>
83+
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
84+
</action>
7385
</policyconfig>

data/polkit/rules.d/gamemode.rules.in

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
/*
2-
* Allow users in privileged gamemode group to run cpugovctl &
3-
* gpuclockctl without authentication
2+
* Allow users in privileged gamemode group to run gamemode utilities
3+
* (cpugovctl, gpuclockctl, cpucorectl, procsysctl, platprofctl, x3dmodectl)
4+
* without authentication
45
*/
56
polkit.addRule(function (action, subject) {
67
if ((action.id == "com.feralinteractive.GameMode.governor-helper" ||
78
action.id == "com.feralinteractive.GameMode.gpu-helper" ||
89
action.id == "com.feralinteractive.GameMode.cpu-helper" ||
910
action.id == "com.feralinteractive.GameMode.procsys-helper" ||
10-
action.id == "com.feralinteractive.GameMode.profile-helper") &&
11+
action.id == "com.feralinteractive.GameMode.profile-helper" ||
12+
action.id == "com.feralinteractive.GameMode.x3dmode-helper") &&
1113
subject.isInGroup("@GAMEMODE_PRIVILEGED_GROUP@"))
1214
{
1315
return polkit.Result.YES;

0 commit comments

Comments
 (0)