mirror of
				https://github.com/FeralInteractive/gamemode.git
				synced 2025-11-04 16:04:20 +01:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -115,6 +115,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];
 | 
			
		||||
@@ -243,6 +245,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;
 | 
			
		||||
 | 
			
		||||
@@ -319,6 +338,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 */
 | 
			
		||||
@@ -866,6 +889,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
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -127,6 +127,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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 */
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -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);
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -101,6 +101,18 @@ disable_splitlock=1
 | 
			
		||||
;park_cores=no
 | 
			
		||||
;pin_cores=yes
 | 
			
		||||
 | 
			
		||||
; AMD 3D V-Cache Performance Optimizer Driver settings
 | 
			
		||||
; These options control the cache mode for dual CCD X3D CPUs (7950x3d, 9950x3d, etc.)
 | 
			
		||||
; "frequency" mode prioritizes higher boost clocks, "cache" mode prioritizes 3D V-Cache performance
 | 
			
		||||
; Allows for dynamically shifting other processes onto a different CCD. E.g. amd_x3d_mode_default=cache may be
 | 
			
		||||
; preferred for some normal, non-game workloads that are better optimized for cache, but
 | 
			
		||||
; amd_x3d_mode_desired=frequency can shift everything but the game process to frequency CCD while GameMode is
 | 
			
		||||
; running, in conjunction with core pinning.
 | 
			
		||||
; Only works on systems with the AMD X3D mode driver (automatically detected)
 | 
			
		||||
; The desired mode is set when entering gamemode, default mode is restored when leaving
 | 
			
		||||
;amd_x3d_mode_desired=frequency
 | 
			
		||||
;amd_x3d_mode_default=cache
 | 
			
		||||
 | 
			
		||||
[supervisor]
 | 
			
		||||
; This section controls the new gamemode functions gamemode_request_start_for and gamemode_request_end_for
 | 
			
		||||
; The whilelist and blacklist control which supervisor programs are allowed to make the above requests
 | 
			
		||||
 
 | 
			
		||||
@@ -73,3 +73,18 @@ platprofctl = executable(
 | 
			
		||||
    install: true,
 | 
			
		||||
    install_dir: path_libexecdir,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Small target util to get and set AMD X3D cache mode
 | 
			
		||||
x3dmodectl_sources = [
 | 
			
		||||
    'x3dmodectl.c',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
x3dmodectl = executable(
 | 
			
		||||
    'x3dmodectl',
 | 
			
		||||
    sources: x3dmodectl_sources,
 | 
			
		||||
    dependencies: [
 | 
			
		||||
        link_daemon_common,
 | 
			
		||||
    ],
 | 
			
		||||
    install: true,
 | 
			
		||||
    install_dir: path_libexecdir,
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										171
									
								
								util/x3dmodectl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								util/x3dmodectl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
 * Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
   this list of conditions and the following disclaimer.
 | 
			
		||||
 * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
   notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
   documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * Neither the name of Feral Interactive nor the names of its contributors
 | 
			
		||||
   may be used to endorse or promote products derived from this software
 | 
			
		||||
   without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
 | 
			
		||||
#include "common-logging.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <glob.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#define X3D_MODE_GLOB_PATTERN "/sys/bus/platform/drivers/amd_x3d_vcache/*/amd_x3d_mode"
 | 
			
		||||
 | 
			
		||||
static char x3d_mode_path[PATH_MAX] = { 0 };
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find and set the x3d mode sysfs path
 | 
			
		||||
 */
 | 
			
		||||
static bool find_x3d_mode_path(void)
 | 
			
		||||
{
 | 
			
		||||
	if (x3d_mode_path[0] != '\0') {
 | 
			
		||||
		return access(x3d_mode_path, F_OK) == 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	glob_t glob_result;
 | 
			
		||||
	if (glob(X3D_MODE_GLOB_PATTERN, GLOB_NOSORT, NULL, &glob_result) != 0) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (glob_result.gl_pathc > 0) {
 | 
			
		||||
		strncpy(x3d_mode_path, glob_result.gl_pathv[0], PATH_MAX - 1);
 | 
			
		||||
		x3d_mode_path[PATH_MAX - 1] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	globfree(&glob_result);
 | 
			
		||||
	return x3d_mode_path[0] != '\0' && access(x3d_mode_path, F_OK) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check if x3d mode control is available
 | 
			
		||||
 */
 | 
			
		||||
static bool x3d_mode_available(void)
 | 
			
		||||
{
 | 
			
		||||
	return find_x3d_mode_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return the current x3d mode
 | 
			
		||||
 */
 | 
			
		||||
static const char *get_x3d_mode(void)
 | 
			
		||||
{
 | 
			
		||||
	static char mode[64] = { 0 };
 | 
			
		||||
	memset(mode, 0, sizeof(mode));
 | 
			
		||||
 | 
			
		||||
	if (!x3d_mode_available()) {
 | 
			
		||||
		return "unavailable";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FILE *f = fopen(x3d_mode_path, "r");
 | 
			
		||||
	if (!f) {
 | 
			
		||||
		LOG_ERROR("Failed to open x3d mode file for read %s: %s\n", x3d_mode_path, strerror(errno));
 | 
			
		||||
		return "error";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fgets(mode, sizeof(mode), f) != NULL) {
 | 
			
		||||
		/* Remove trailing newline */
 | 
			
		||||
		char *newline = strchr(mode, '\n');
 | 
			
		||||
		if (newline) {
 | 
			
		||||
			*newline = '\0';
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		LOG_ERROR("Failed to read x3d mode from %s: %s\n", x3d_mode_path, strerror(errno));
 | 
			
		||||
		fclose(f);
 | 
			
		||||
		return "error";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fclose(f);
 | 
			
		||||
	return mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the x3d mode to the specified value
 | 
			
		||||
 */
 | 
			
		||||
static int set_x3d_mode(const char *value)
 | 
			
		||||
{
 | 
			
		||||
	if (!x3d_mode_available()) {
 | 
			
		||||
		LOG_ERROR("AMD x3D mode control is not available on this system\n");
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Validate the mode value */
 | 
			
		||||
	if (strcmp(value, "frequency") != 0 && strcmp(value, "cache") != 0) {
 | 
			
		||||
		LOG_ERROR("Invalid x3d mode '%s'. Valid modes are 'frequency' or 'cache'\n", value);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FILE *f = fopen(x3d_mode_path, "w");
 | 
			
		||||
	if (!f) {
 | 
			
		||||
		LOG_ERROR("Failed to open x3d mode file for write %s: %s\n",
 | 
			
		||||
		          x3d_mode_path,
 | 
			
		||||
		          strerror(errno));
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int res = fprintf(f, "%s\n", value);
 | 
			
		||||
	if (res < 0) {
 | 
			
		||||
		LOG_ERROR("Failed to set x3d mode to %s: %s\n", value, strerror(errno));
 | 
			
		||||
		fclose(f);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fclose(f);
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main entry point, dispatch to the appropriate helper
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	if (argc == 2 && strncmp(argv[1], "get", 3) == 0) {
 | 
			
		||||
		printf("%s", get_x3d_mode());
 | 
			
		||||
	} else if (argc == 3 && strncmp(argv[1], "set", 3) == 0) {
 | 
			
		||||
		const char *value = argv[2];
 | 
			
		||||
 | 
			
		||||
		if (geteuid() != 0) {
 | 
			
		||||
			LOG_ERROR("This program must be run as root\n");
 | 
			
		||||
			return EXIT_FAILURE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return set_x3d_mode(value);
 | 
			
		||||
	} else {
 | 
			
		||||
		fprintf(stderr, "usage: x3dmodectl [get] [set VALUE]\n");
 | 
			
		||||
		fprintf(stderr, "where VALUE can be 'frequency' or 'cache'\n");
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user