Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions daemon/gamemode-config.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ struct GameModeConfig {

char cpu_park_cores[CONFIG_VALUE_MAX];
char cpu_pin_cores[CONFIG_VALUE_MAX];
char amd_x3d_mode_desired[CONFIG_VALUE_MAX];
char amd_x3d_mode_default[CONFIG_VALUE_MAX];

long require_supervisor;
char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
Expand Down Expand Up @@ -242,6 +244,23 @@ static bool get_string_value(const char *value, char output[CONFIG_VALUE_MAX])
return true;
}

/*
* Get and validate an x3d mode value
*/
static bool get_x3d_mode_value(const char *name, const char *value, char output[CONFIG_VALUE_MAX])
{
if (strcmp(value, "frequency") != 0 && strcmp(value, "cache") != 0) {
LOG_ERROR("Config: %s has invalid value '%s'. Valid values are 'frequency' or 'cache'\n",
name,
value);
return false;
}

strncpy(output, value, CONFIG_VALUE_MAX - 1);
output[CONFIG_VALUE_MAX - 1] = '\0';
return true;
}

/* Controls whether to read the protected config variables */
static bool load_protected = false;

Expand Down Expand Up @@ -316,6 +335,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.cpu_park_cores);
} else if (strcmp(name, "pin_cores") == 0) {
valid = get_string_value(value, self->values.cpu_pin_cores);
} else if (strcmp(name, "amd_x3d_mode_desired") == 0) {
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_desired);
} else if (strcmp(name, "amd_x3d_mode_default") == 0) {
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_default);
}
} else if (strcmp(section, "supervisor") == 0) {
/* Supervisor subsection */
Expand Down Expand Up @@ -861,6 +884,22 @@ void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]
sizeof(self->values.cpu_pin_cores));
}

void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.amd_x3d_mode_desired,
sizeof(self->values.amd_x3d_mode_desired));
}

void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.amd_x3d_mode_default,
sizeof(self->values.amd_x3d_mode_default));
}

/*
* Checks if the supervisor is whitelisted
*/
Expand Down
2 changes: 2 additions & 0 deletions daemon/gamemode-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VA
*/
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);

/**
* Functions to get supervisor config permissions
Expand Down
94 changes: 94 additions & 0 deletions daemon/gamemode-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ struct GameModeContext {

long initial_split_lock_mitigate;

char initial_x3d_mode[64]; /**<Initial x3d mode to restore */

/* Reaper control */
struct {
pthread_t thread;
Expand Down Expand Up @@ -168,6 +170,9 @@ void game_mode_context_init(GameModeContext *self)

self->initial_split_lock_mitigate = -1;

/* clear the initial x3d mode string */
memset(self->initial_x3d_mode, 0, sizeof(self->initial_x3d_mode));

pthread_rwlock_init(&self->rwlock, NULL);

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

static void game_mode_store_x3d_mode(GameModeContext *self)
{
char x3d_mode_desired[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_desired(self->config, x3d_mode_desired);
if (x3d_mode_desired[0] == '\0') {
return;
}

if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
LOG_MSG("x3dmodectl utility not found, X3D mode control disabled\n");
return;
}

const char *const exec_args[] = {
LIBEXECDIR "/x3dmodectl",
"get",
NULL,
};

char output[EXTERNAL_BUFFER_MAX] = { 0 };
int ret = run_external_process(exec_args, output, -1);
if (ret != 0) {
LOG_MSG("X3D mode hardware not available or failed to get current mode\n");
return;
}

strncpy(self->initial_x3d_mode, output, sizeof(self->initial_x3d_mode) - 1);
self->initial_x3d_mode[sizeof(self->initial_x3d_mode) - 1] = '\0';
char *newline = strchr(self->initial_x3d_mode, '\n');
if (newline) {
*newline = '\0';
}

LOG_MSG("x3d mode was initially set to [%s]\n", self->initial_x3d_mode);
}

static int game_mode_set_x3d_mode(GameModeContext *self, bool desired)
{
char x3d_mode_config[CONFIG_VALUE_MAX] = { 0 };

if (desired) {
config_get_amd_x3d_mode_desired(self->config, x3d_mode_config);
} else {
config_get_amd_x3d_mode_default(self->config, x3d_mode_config);
if (x3d_mode_config[0] == '\0') {
if (self->initial_x3d_mode[0] != '\0') {
strncpy(x3d_mode_config, self->initial_x3d_mode, CONFIG_VALUE_MAX - 1);
x3d_mode_config[CONFIG_VALUE_MAX - 1] = '\0';
} else {
return 0;
}
}
}

if (x3d_mode_config[0] == '\0') {
return 0;
}

if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
LOG_MSG("x3dmodectl utility not found, skipping X3D mode change\n");
return 0;
}

if (strcmp(x3d_mode_config, "frequency") != 0 && strcmp(x3d_mode_config, "cache") != 0) {
LOG_ERROR("Invalid X3D mode '%s'. Valid modes are 'frequency' or 'cache'\n",
x3d_mode_config);
return -1;
}

const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/x3dmodectl", "set", x3d_mode_config, NULL,
};

LOG_MSG("Requesting update of X3D mode to %s\n", x3d_mode_config);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to update X3D mode\n");
return ret;
}

return 0;
}

static void game_mode_store_governor(GameModeContext *self)
{
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
Expand Down Expand Up @@ -468,6 +556,8 @@ static void game_mode_context_store_defaults(GameModeContext *self)
game_mode_store_governor(self);

game_mode_store_splitlock(self);

game_mode_store_x3d_mode(self);
}

/**
Expand Down Expand Up @@ -504,6 +594,8 @@ static void game_mode_context_enter(GameModeContext *self)

game_mode_disable_splitlock(self, true);

game_mode_set_x3d_mode(self, true);

/* Apply GPU optimisations by first getting the current values, and then setting the target */
game_mode_get_gpu(self->stored_gpu);
game_mode_apply_gpu(self->target_gpu);
Expand Down Expand Up @@ -548,6 +640,8 @@ static void game_mode_context_leave(GameModeContext *self)

game_mode_disable_splitlock(self, false);

game_mode_set_x3d_mode(self, false);

game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);

game_mode_disable_igpu_optimization(self);
Expand Down
115 changes: 115 additions & 0 deletions daemon/gamemode-tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "gamemode-config.h"
#include "gamemode_client.h"

#include "build-config.h"

#include <pthread.h>
#include <sys/syscall.h>
#include <sys/wait.h>
Expand Down Expand Up @@ -837,6 +839,104 @@ int run_ioprio_tests(struct GameModeConfig *config)
return ret;
}

/* Check the AMD X3D mode setting works */
static int run_x3d_mode_tests(struct GameModeConfig *config)
{
/* Get the two config parameters we care about */
char desired_mode[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_desired(config, desired_mode);

if (desired_mode[0] == '\0') {
/* Not configured */
return 1;
}

char default_mode[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_default(config, default_mode);

/* Get the initial X3D mode state */
char initial_mode[64] = { 0 };
const char *const get_args[] = {
LIBEXECDIR "/x3dmodectl",
"get",
NULL,
};

char output[EXTERNAL_BUFFER_MAX] = { 0 };
int ret = run_external_process(get_args, output, -1);
if (ret != 0) {
return 1;
}

/* Store the initial mode, removing any trailing newline */
strncpy(initial_mode, output, sizeof(initial_mode) - 1);
initial_mode[sizeof(initial_mode) - 1] = '\0';
char *newline = strchr(initial_mode, '\n');
if (newline) {
*newline = '\0';
}

/* Check if hardware is available */
if (strcmp(initial_mode, "unavailable") == 0) {
return 1;
}

/* Start gamemode */
gamemode_request_start();

/* Give gamemode time to apply settings */
usleep(500000);

/* Verify the mode is the desired one */
ret = run_external_process(get_args, output, -1);
if (ret != 0) {
LOG_ERROR("Failed to get X3D mode after gamemode start\n");
gamemode_request_end();
return -1;
}

/* Remove trailing newline from output */
newline = strchr(output, '\n');
if (newline) {
*newline = '\0';
}

if (strcmp(output, desired_mode) != 0) {
LOG_ERROR("X3D mode was not set to %s (was actually %s)!\n", desired_mode, output);
gamemode_request_end();
return -1;
}

/* End gamemode */
gamemode_request_end();

/* Give gamemode time to restore settings */
usleep(500000);

/* Verify the mode is restored */
ret = run_external_process(get_args, output, -1);
if (ret != 0) {
LOG_ERROR("Failed to get X3D mode after gamemode end\n");
return -1;
}

/* Remove trailing newline from output */
newline = strchr(output, '\n');
if (newline) {
*newline = '\0';
}

/* Determine expected restored mode */
const char *expected_mode = (default_mode[0] != '\0') ? default_mode : initial_mode;

if (strcmp(output, expected_mode) != 0) {
LOG_ERROR("X3D mode was not restored to %s (was actually %s)!\n", expected_mode, output);
return -1;
}

return 0;
}

/**
* game_mode_run_feature_tests runs a set of tests for each current feature (based on the current
* config) returns 0 for success, -1 for failure
Expand Down Expand Up @@ -947,6 +1047,21 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
}
}

/* Was the AMD X3D mode changed? */
{
LOG_MSG("::: Verifying AMD X3D mode\n");
int x3dstatus = run_x3d_mode_tests(config);

if (x3dstatus == 1)
LOG_MSG("::: Passed (AMD X3D mode not configured)\n");
else if (x3dstatus == 0)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
status = -1;
}
}

/* TODO */
/* Was the scheduling applied and removed? Does it get applied to a full process tree? */
/* Does the screensaver get inhibited? Unknown if this is testable, org.freedesktop.ScreenSaver
Expand Down
12 changes: 12 additions & 0 deletions data/polkit/actions/com.feralinteractive.GameMode.policy.in
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,16 @@
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/platprofctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>

<action id="com.feralinteractive.GameMode.x3dmode-helper">
<description>Modify the AMD X3D cache mode</description>
<message>Authentication is required to modify AMD X3D cache mode</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/x3dmodectl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>
8 changes: 5 additions & 3 deletions data/polkit/rules.d/gamemode.rules.in
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/*
* Allow users in privileged gamemode group to run cpugovctl &
* gpuclockctl without authentication
* Allow users in privileged gamemode group to run gamemode utilities
* (cpugovctl, gpuclockctl, cpucorectl, procsysctl, platprofctl, x3dmodectl)
* without authentication
*/
polkit.addRule(function (action, subject) {
if ((action.id == "com.feralinteractive.GameMode.governor-helper" ||
action.id == "com.feralinteractive.GameMode.gpu-helper" ||
action.id == "com.feralinteractive.GameMode.cpu-helper" ||
action.id == "com.feralinteractive.GameMode.procsys-helper" ||
action.id == "com.feralinteractive.GameMode.profile-helper") &&
action.id == "com.feralinteractive.GameMode.profile-helper" ||
action.id == "com.feralinteractive.GameMode.x3dmode-helper") &&
subject.isInGroup("@GAMEMODE_PRIVILEGED_GROUP@"))
{
return polkit.Result.YES;
Expand Down
Loading