From aeaef7377df4b856d990bb2ea7e3eadbc28c6d21 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 30 Jan 2019 20:25:29 +0000 Subject: [PATCH 01/42] Preliminary (empty) implementation of GPU optimisations --- daemon/gamemode-gpu.c | 139 ++++++++++++++++++++++++++++++++++++++++++ daemon/gamemode.c | 14 +++++ daemon/gamemode.h | 8 +++ daemon/meson.build | 1 + 4 files changed, 162 insertions(+) create mode 100644 daemon/gamemode-gpu.c diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c new file mode 100644 index 0000000..bfd4fa9 --- /dev/null +++ b/daemon/gamemode-gpu.c @@ -0,0 +1,139 @@ + +/* + +Copyright (c) 2017-2018, Feral Interactive +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 "gamemode.h" +#include "helpers.h" +#include "logging.h" + +// TODO +// Gather GPU type and information +// Allow configuration file specifying of gpu info +// Apply Nvidia GPU settings (CoolBits will be needed) +// Apply AMD GPU settings (Will need user changing pwm1_enable) +// Intel? +// Provide documentation on optimisations + +/* Enums for GPU vendors */ +enum GPUVendor { + Vendor_Invalid = 0, + Vendor_NVIDIA = 0x10de, + Vendor_AMD = 0x1002, + Vendor_Intel = 0x8086 +}; + +/* Storage for static GPU info gathered at start */ +struct GameModeGPUInfo { + enum GPUVendor vendor; + int device; /* path to device, ie. /sys/class/drm/card#/ */ +}; + +/** + * Applies or removes Nvidia optimisations + */ +static int apply_gpu_nvidia(bool apply) +{ + // Running these commands: + // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' + // nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' + if (apply) { + } else { + } + + return 0; +} + +/** + * Applies or removes AMD optimisations + */ +static int apply_gpu_amd(bool apply) +{ + // We'll want to set both the following: + // core: device/pp_sclk_od (0%+ additional) + // mem: device/pp_mclk_od (0%+ additional) + // Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ + if (apply) { + } else { + } + + return 0; +} + +/** + * Attempts to identify the current in use GPU information + */ +int game_mode_identify_gpu(GameModeGPUInfo **info) +{ + int status = 0; + + /* Verify input, this is programmer error */ + if (!info || *info) + FATAL_ERROR("Invalid GameModeGPUInfo passed to %s", __func__); + + /* Create the context */ + GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); + memset(new_info, 0, sizeof(GameModeGPUInfo)); + + // TODO: Fill in the GPU info + + /* Give back the new gpu info */ + *info = new_info; + + return status; +} + +/* Simply used to free the GPU info object */ +void game_mode_free_gpu(GameModeGPUInfo **info) +{ + /* Simply free the object */ + free(*info); + *info = NULL; +} + +/** + * Applies GPU optimisations when gamemode is active and removes them after + */ +int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) +{ + switch (info->vendor) { + case Vendor_NVIDIA: + return apply_gpu_nvidia(apply); + case Vendor_AMD: + return apply_gpu_amd(apply); + default: + break; + } + + /* Unsupported GPU vendor, do nothing */ + return 0; +} diff --git a/daemon/gamemode.c b/daemon/gamemode.c index 8c0f3a4..f1479dc 100644 --- a/daemon/gamemode.c +++ b/daemon/gamemode.c @@ -63,6 +63,8 @@ struct GameModeContext { char initial_cpu_mode[64]; /**config = config_create(); config_init(self->config); + /* Grab the current GPU */ + game_mode_identify_gpu(&self->gpu_info); + pthread_rwlock_init(&self->rwlock, NULL); pthread_mutex_init(&self->reaper.mutex, NULL); pthread_cond_init(&self->reaper.condition, NULL); @@ -144,6 +149,9 @@ void game_mode_context_destroy(GameModeContext *self) pthread_cond_destroy(&self->reaper.condition); pthread_mutex_destroy(&self->reaper.mutex); + /* Destroy the gpu object */ + game_mode_free_gpu(&self->gpu_info); + /* Destroy the config object */ config_destroy(self->config); @@ -200,6 +208,9 @@ static void game_mode_context_enter(GameModeContext *self) /* Inhibit the screensaver */ if (config_get_inhibit_screensaver(self->config)) game_mode_inhibit_screensaver(true); + + /* Apply GPU optimisations */ + game_mode_apply_gpu(self->gpu_info, true); } /** @@ -213,6 +224,9 @@ static void game_mode_context_leave(GameModeContext *self) LOG_MSG("Leaving Game Mode...\n"); sd_notifyf(0, "STATUS=%sGameMode is currently deactivated.%s\n", "\x1B[1;36m", "\x1B[0m"); + /* Remove GPU optimisations */ + game_mode_apply_gpu(self->gpu_info, false); + /* UnInhibit the screensaver */ if (config_get_inhibit_screensaver(self->config)) game_mode_inhibit_screensaver(false); diff --git a/daemon/gamemode.h b/daemon/gamemode.h index 337c95f..6304e2f 100644 --- a/daemon/gamemode.h +++ b/daemon/gamemode.h @@ -136,3 +136,11 @@ char *game_mode_resolve_wine_preloader(const pid_t pid); * Provides a test suite to verify gamemode behaviour */ int game_mode_run_client_tests(void); + +/** gamemode-gpu.c + * Provides internal APU functions to apply optimisations to gpus + */ +typedef struct GameModeGPUInfo GameModeGPUInfo; +int game_mode_identify_gpu(GameModeGPUInfo **info); +void game_mode_free_gpu(GameModeGPUInfo **info); +int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply); diff --git a/daemon/meson.build b/daemon/meson.build index 02a4519..f5b054a 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -24,6 +24,7 @@ daemon_sources = [ 'gamemode-sched.c', 'gamemode-wine.c', 'gamemode-tests.c', + 'gamemode-gpu.c', 'daemonize.c', 'dbus_messaging.c', 'governors.c', From b1bf33d386f5176d223f857b8a504324a559f81c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 30 Jan 2019 21:12:11 +0000 Subject: [PATCH 02/42] Add config parameters for GPU clocking --- daemon/daemon_config.c | 52 ++++++++++++++++++++++++++++++++++++++++++ daemon/daemon_config.h | 9 ++++++++ daemon/gamemode-gpu.c | 45 ++++++++++++++++++++++++++++++++---- daemon/gamemode.c | 4 ++-- daemon/gamemode.h | 2 +- example/gamemode.ini | 16 +++++++++++++ 6 files changed, 120 insertions(+), 8 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 3729bfe..c9bd11a 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -75,6 +75,12 @@ struct GameModeConfig { long inhibit_screensaver; long reaper_frequency; + + long apply_gpu_optimisations; + long nv_core_clock_mhz_offset; + long nv_mem_clock_mhz_offset; + long amd_core_clock_percentage; + long amd_mem_clock_percentage; }; /* @@ -173,6 +179,19 @@ static int inih_handler(void *user, const char *section, const char *name, const } else if (strcmp(name, "inhibit_screensaver") == 0) { valid = get_long_value(name, value, &self->inhibit_screensaver); } + } else if (strcmp(section, "gpu") == 0) { + /* GPU subsection */ + if (strcmp(name, "apply_gpu_optimisations") == 0) { + valid = get_long_value(name, value, &self->apply_gpu_optimisations); + } else if (strcmp(name, "nv_core_clock_mhz_offset") == 0) { + valid = get_long_value(name, value, &self->nv_core_clock_mhz_offset); + } else if (strcmp(name, "nv_mem_clock_mhz_offset") == 0) { + valid = get_long_value(name, value, &self->nv_mem_clock_mhz_offset); + } else if (strcmp(name, "amd_core_clock_percentage") == 0) { + valid = get_long_value(name, value, &self->amd_core_clock_percentage); + } else if (strcmp(name, "amd_mem_clock_percentage") == 0) { + valid = get_long_value(name, value, &self->amd_mem_clock_percentage); + } } else if (strcmp(section, "custom") == 0) { /* Custom subsection */ if (strcmp(name, "start") == 0) { @@ -234,6 +253,11 @@ static void load_config_files(GameModeConfig *self) self->renice = 4; /* default value of 4 */ self->reaper_frequency = DEFAULT_REAPER_FREQ; self->inhibit_screensaver = 1; /* Defaults to on */ + self->apply_gpu_optimisations = 0; + self->nv_core_clock_mhz_offset = 0; + self->nv_mem_clock_mhz_offset = 0; + self->amd_core_clock_percentage = 0; + self->amd_mem_clock_percentage = 0; /* * Locations to load, in order @@ -461,3 +485,31 @@ void config_get_ioprio_value(GameModeConfig *self, int *value) else *value = atoi(ioprio_value); } + +/* + * Get various config info for gpu optimisations + */ +void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->apply_gpu_optimisations, sizeof(long)); +} + +void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->nv_core_clock_mhz_offset, sizeof(long)); +} + +void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->nv_mem_clock_mhz_offset, sizeof(long)); +} + +void config_get_amd_core_clock_percentage(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->amd_core_clock_percentage, sizeof(long)); +} + +void config_get_amd_mem_clock_percentage(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->amd_mem_clock_percentage, sizeof(long)); +} diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index 608401b..0253c28 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -129,3 +129,12 @@ void config_get_renice_value(GameModeConfig *self, long *value); * Get the ioprio value */ void config_get_ioprio_value(GameModeConfig *self, int *value); + +/* + * Get various config info for gpu optimisations + */ +void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value); +void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value); +void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value); +void config_get_amd_core_clock_percentage(GameModeConfig *self, long *value); +void config_get_amd_mem_clock_percentage(GameModeConfig *self, long *value); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index bfd4fa9..5bf86c9 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -36,6 +36,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "helpers.h" #include "logging.h" +#include "daemon_config.h" + // TODO // Gather GPU type and information // Allow configuration file specifying of gpu info @@ -56,13 +58,19 @@ enum GPUVendor { struct GameModeGPUInfo { enum GPUVendor vendor; int device; /* path to device, ie. /sys/class/drm/card#/ */ + + long core; /* Core clock to apply */ + long mem; /* Mem clock to apply */ }; /** * Applies or removes Nvidia optimisations */ -static int apply_gpu_nvidia(bool apply) +static int apply_gpu_nvidia(const GameModeGPUInfo *info, bool apply) { + if (!info) + return 0; + // Running these commands: // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' // nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' @@ -76,8 +84,11 @@ static int apply_gpu_nvidia(bool apply) /** * Applies or removes AMD optimisations */ -static int apply_gpu_amd(bool apply) +static int apply_gpu_amd(const GameModeGPUInfo *info, bool apply) { + if (!info) + return 0; + // We'll want to set both the following: // core: device/pp_sclk_od (0%+ additional) // mem: device/pp_mclk_od (0%+ additional) @@ -92,7 +103,7 @@ static int apply_gpu_amd(bool apply) /** * Attempts to identify the current in use GPU information */ -int game_mode_identify_gpu(GameModeGPUInfo **info) +int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) { int status = 0; @@ -100,12 +111,32 @@ int game_mode_identify_gpu(GameModeGPUInfo **info) if (!info || *info) FATAL_ERROR("Invalid GameModeGPUInfo passed to %s", __func__); + /* Early out if we have this feature turned off */ + long apply = 0; + config_get_apply_gpu_optimisations(config, &apply); + if (apply == 0) + return 0; + /* Create the context */ GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); memset(new_info, 0, sizeof(GameModeGPUInfo)); // TODO: Fill in the GPU info + /* Load the config based on GPU */ + switch (new_info->vendor) { + case Vendor_NVIDIA: + config_get_nv_core_clock_mhz_offset(config, &new_info->core); + config_get_nv_mem_clock_mhz_offset(config, &new_info->mem); + break; + case Vendor_AMD: + config_get_amd_core_clock_percentage(config, &new_info->core); + config_get_amd_mem_clock_percentage(config, &new_info->mem); + break; + default: + break; + } + /* Give back the new gpu info */ *info = new_info; @@ -125,11 +156,15 @@ void game_mode_free_gpu(GameModeGPUInfo **info) */ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) { + // Null info means don't apply anything + if (!info) + return 0; + switch (info->vendor) { case Vendor_NVIDIA: - return apply_gpu_nvidia(apply); + return apply_gpu_nvidia(info, apply); case Vendor_AMD: - return apply_gpu_amd(apply); + return apply_gpu_amd(info, apply); default: break; } diff --git a/daemon/gamemode.c b/daemon/gamemode.c index f1479dc..2b80057 100644 --- a/daemon/gamemode.c +++ b/daemon/gamemode.c @@ -109,8 +109,8 @@ void game_mode_context_init(GameModeContext *self) self->config = config_create(); config_init(self->config); - /* Grab the current GPU */ - game_mode_identify_gpu(&self->gpu_info); + /* Initialise the current GPU info */ + game_mode_initialise_gpu(self->config, &self->gpu_info); pthread_rwlock_init(&self->rwlock, NULL); pthread_mutex_init(&self->reaper.mutex, NULL); diff --git a/daemon/gamemode.h b/daemon/gamemode.h index 6304e2f..c70c048 100644 --- a/daemon/gamemode.h +++ b/daemon/gamemode.h @@ -141,6 +141,6 @@ int game_mode_run_client_tests(void); * Provides internal APU functions to apply optimisations to gpus */ typedef struct GameModeGPUInfo GameModeGPUInfo; -int game_mode_identify_gpu(GameModeGPUInfo **info); +int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info); void game_mode_free_gpu(GameModeGPUInfo **info); int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply); diff --git a/example/gamemode.ini b/example/gamemode.ini index f6a73a4..f366e46 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -34,6 +34,22 @@ inhibit_screensaver=1 ;blacklist=HalfLife3 ; glxgears +[gpu] +; Here Be Dragons! +; Warning: Use these settings at your own risk +; Any damage to hardware incurred due to this feature is your responsibility and yours alone + +; Setting this to 1 will allow gamemode to attempt to apply GPU optimisations such as overclocks +;apply_gpu_optimisations=0 + +; Nvidia specific settings (these are Mhz offsets from the baseline, ie. 0 applies no change) +;nv_core_clock_mhz_offset=0 +;nv_mem_clock_mhz_offset=0 + +; AMD specific settings (these are percentages applied on top of the baseline, ie. 0 applies no change) +;amd_core_clock_percentage=0 +;amd_mem_clock_percentage=0 + [custom] ; Custom scripts (executed using the shell) when gamemode starts and ends ;start=notify-send "GameMode started" From 8d4e9ac54e63619edc548109d9a69a5a685d0099 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 31 Jan 2019 17:27:50 +0000 Subject: [PATCH 03/42] Add gpuclockctl to allow privilaged control of GPU parameters --- daemon/gamemode-gpu.c | 69 ++---------------- daemon/gpu-query.c | 64 +++++++++++++++++ daemon/gpu-query.h | 61 ++++++++++++++++ daemon/gpuclockctl.c | 76 ++++++++++++++++++++ daemon/meson.build | 16 +++++ data/com.feralinteractive.GameMode.policy.in | 11 +++ 6 files changed, 232 insertions(+), 65 deletions(-) create mode 100644 daemon/gpu-query.c create mode 100644 daemon/gpu-query.h create mode 100644 daemon/gpuclockctl.c diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 5bf86c9..386666f 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -37,69 +37,16 @@ POSSIBILITY OF SUCH DAMAGE. #include "logging.h" #include "daemon_config.h" +#include "gpu-query.h" // TODO // Gather GPU type and information -// Allow configuration file specifying of gpu info +// Allow override of vendor and device // Apply Nvidia GPU settings (CoolBits will be needed) // Apply AMD GPU settings (Will need user changing pwm1_enable) // Intel? // Provide documentation on optimisations -/* Enums for GPU vendors */ -enum GPUVendor { - Vendor_Invalid = 0, - Vendor_NVIDIA = 0x10de, - Vendor_AMD = 0x1002, - Vendor_Intel = 0x8086 -}; - -/* Storage for static GPU info gathered at start */ -struct GameModeGPUInfo { - enum GPUVendor vendor; - int device; /* path to device, ie. /sys/class/drm/card#/ */ - - long core; /* Core clock to apply */ - long mem; /* Mem clock to apply */ -}; - -/** - * Applies or removes Nvidia optimisations - */ -static int apply_gpu_nvidia(const GameModeGPUInfo *info, bool apply) -{ - if (!info) - return 0; - - // Running these commands: - // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' - // nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' - if (apply) { - } else { - } - - return 0; -} - -/** - * Applies or removes AMD optimisations - */ -static int apply_gpu_amd(const GameModeGPUInfo *info, bool apply) -{ - if (!info) - return 0; - - // We'll want to set both the following: - // core: device/pp_sclk_od (0%+ additional) - // mem: device/pp_mclk_od (0%+ additional) - // Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ - if (apply) { - } else { - } - - return 0; -} - /** * Attempts to identify the current in use GPU information */ @@ -121,7 +68,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); memset(new_info, 0, sizeof(GameModeGPUInfo)); - // TODO: Fill in the GPU info + // TODO: Fill in the GPU vendor and device /* Load the config based on GPU */ switch (new_info->vendor) { @@ -160,15 +107,7 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) if (!info) return 0; - switch (info->vendor) { - case Vendor_NVIDIA: - return apply_gpu_nvidia(info, apply); - case Vendor_AMD: - return apply_gpu_amd(info, apply); - default: - break; - } + /* TODO Call gpuclockctl set */ - /* Unsupported GPU vendor, do nothing */ return 0; } diff --git a/daemon/gpu-query.c b/daemon/gpu-query.c new file mode 100644 index 0000000..7bf515c --- /dev/null +++ b/daemon/gpu-query.c @@ -0,0 +1,64 @@ +/* + +Copyright (c) 2017-2018, Feral Interactive +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 "gpu-query.h" +#include "logging.h" + +// NVIDIA +// Running these commands: +// nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' +// nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' + +// AMD +// We'll want to set both the following: +// core: device/pp_sclk_od (0%+ additional) +// mem: device/pp_mclk_od (0%+ additional) +// Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ + +/** + * Get the gpu state + * Populates the struct with the GPU info on the system + */ +int get_gpu_state( struct GameModeGPUInfo* info ) +{ + return 0; +} + +/** + * Set the gpu state based on input parameters + * Only works when run as root + */ +int set_gpu_state( struct GameModeGPUInfo* info ) +{ + return 0; +} diff --git a/daemon/gpu-query.h b/daemon/gpu-query.h new file mode 100644 index 0000000..cab5214 --- /dev/null +++ b/daemon/gpu-query.h @@ -0,0 +1,61 @@ +/* + +Copyright (c) 2017-2018, Feral Interactive +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. + + */ + +#pragma once + +/* Enums for GPU vendors */ +enum GPUVendor { + Vendor_Invalid = 0, + Vendor_NVIDIA = 0x10de, + Vendor_AMD = 0x1002, + Vendor_Intel = 0x8086 +}; + +/* Storage for GPU info*/ +struct GameModeGPUInfo { + enum GPUVendor vendor; + int device; /* path to device, ie. /sys/class/drm/card#/ */ + + long core; /* Core clock to apply */ + long mem; /* Mem clock to apply */ +}; + +/** + * Get the gpu state + * Populates the struct with the GPU info on the system + */ +int get_gpu_state( struct GameModeGPUInfo* info ); + +/** + * Set the gpu state based on input parameters + * Only works when run as root + */ +int set_gpu_state( struct GameModeGPUInfo* info ); diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c new file mode 100644 index 0000000..f954815 --- /dev/null +++ b/daemon/gpuclockctl.c @@ -0,0 +1,76 @@ +/* + +Copyright (c) 2017-2018, Feral Interactive +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 "logging.h" + +#include "gpu-query.h" + +/** + * Main entry point, dispatch to the appropriate helper + */ +int main(int argc, char *argv[]) +{ + if (argc == 4 && strncmp(argv[1], "get", 3) == 0) { + const char *vendor = argv[1]; + const char *device = argv[2]; + + struct GameModeGPUInfo info; + /* TODO Populate with vendor and device */ + + get_gpu_state(&info); + + printf("%d %d\n", info.core, info.mem); + + } else if (argc == 6 && strncmp(argv[3], "set", 3) == 0) { + const char *vendor = argv[1]; + const char *device = argv[2]; + const char *core = argv[4]; + const char *mem = argv[5]; + + /* Must be root to set the state */ + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root\n"); + return EXIT_FAILURE; + } + + struct GameModeGPUInfo info; + /* TODO Populate with vendor, device and clocks */ + + return set_gpu_state(&info); + } else { + fprintf(stderr, "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM]\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/daemon/meson.build b/daemon/meson.build index f5b054a..9b79453 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -2,6 +2,7 @@ common_sources = [ 'logging.c', 'governors-query.c', + 'gpu-query.c', ] daemon_common = static_library( @@ -62,3 +63,18 @@ cpugovctl = executable( install: true, install_dir: path_libexecdir, ) + +# Small target util to get and set gpu clocks values +gpuclockctl_sources = [ + 'gpuclockctl.c', +] + +gpuclockctl = executable( + 'gpuclockctl', + sources: gpuclockctl_sources, + dependencies: [ + link_daemon_common, + ], + install: true, + install_dir: path_libexecdir, +) diff --git a/data/com.feralinteractive.GameMode.policy.in b/data/com.feralinteractive.GameMode.policy.in index 8165162..b0dce0d 100644 --- a/data/com.feralinteractive.GameMode.policy.in +++ b/data/com.feralinteractive.GameMode.policy.in @@ -23,4 +23,15 @@ @LIBEXECDIR@/cpugovctl + + Modify the GPU clock statesr + Authentication is required to modify the GPU clock states + + no + no + yes + + @LIBEXECDIR@/gpuclockctl + + From cee2351c55e21bb6cc3246f7449ea631081e5a06 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 31 Jan 2019 17:35:18 +0000 Subject: [PATCH 04/42] Add config for vendor and device --- daemon/daemon_config.c | 18 ++++++++++++++++++ daemon/daemon_config.h | 2 ++ daemon/gamemode-gpu.c | 6 +++++- daemon/gpu-query.c | 4 ++-- daemon/gpu-query.h | 4 ++-- example/gamemode.ini | 4 ++++ 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index c9bd11a..f342254 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -77,6 +77,8 @@ struct GameModeConfig { long reaper_frequency; long apply_gpu_optimisations; + long gpu_vendor; + long gpu_device; long nv_core_clock_mhz_offset; long nv_mem_clock_mhz_offset; long amd_core_clock_percentage; @@ -183,6 +185,10 @@ static int inih_handler(void *user, const char *section, const char *name, const /* GPU subsection */ if (strcmp(name, "apply_gpu_optimisations") == 0) { valid = get_long_value(name, value, &self->apply_gpu_optimisations); + } else if (strcmp(name, "gpu_vendor") == 0) { + valid = get_long_value(name, value, &self->gpu_device); + } else if (strcmp(name, "gpu_device") == 0) { + valid = get_long_value(name, value, &self->gpu_device); } else if (strcmp(name, "nv_core_clock_mhz_offset") == 0) { valid = get_long_value(name, value, &self->nv_core_clock_mhz_offset); } else if (strcmp(name, "nv_mem_clock_mhz_offset") == 0) { @@ -254,6 +260,8 @@ static void load_config_files(GameModeConfig *self) self->reaper_frequency = DEFAULT_REAPER_FREQ; self->inhibit_screensaver = 1; /* Defaults to on */ self->apply_gpu_optimisations = 0; + self->gpu_vendor = 0; + self->gpu_device = 0; self->nv_core_clock_mhz_offset = 0; self->nv_mem_clock_mhz_offset = 0; self->amd_core_clock_percentage = 0; @@ -494,6 +502,16 @@ void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value) memcpy_locked_config(self, value, &self->apply_gpu_optimisations, sizeof(long)); } +void config_get_gpu_vendor(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->gpu_vendor, sizeof(long)); +} + +void config_get_gpu_device(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->gpu_device, sizeof(long)); +} + void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value) { memcpy_locked_config(self, value, &self->nv_core_clock_mhz_offset, sizeof(long)); diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index 0253c28..21c9fd0 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -134,6 +134,8 @@ void config_get_ioprio_value(GameModeConfig *self, int *value); * Get various config info for gpu optimisations */ void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value); +void config_get_gpu_vendor(GameModeConfig *self, long *value); +void config_get_gpu_device(GameModeConfig *self, long *value); void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value); void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value); void config_get_amd_core_clock_percentage(GameModeConfig *self, long *value); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 386666f..d37e941 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -68,7 +68,11 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); memset(new_info, 0, sizeof(GameModeGPUInfo)); - // TODO: Fill in the GPU vendor and device + // TODO: Fill in the GPU vendor and device automatically + + /* Get the config parameters */ + config_get_gpu_vendor(config, &new_info->vendor); + config_get_gpu_device(config, &new_info->device); /* Load the config based on GPU */ switch (new_info->vendor) { diff --git a/daemon/gpu-query.c b/daemon/gpu-query.c index 7bf515c..0f6b2e2 100644 --- a/daemon/gpu-query.c +++ b/daemon/gpu-query.c @@ -49,7 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. * Get the gpu state * Populates the struct with the GPU info on the system */ -int get_gpu_state( struct GameModeGPUInfo* info ) +int get_gpu_state(struct GameModeGPUInfo *info) { return 0; } @@ -58,7 +58,7 @@ int get_gpu_state( struct GameModeGPUInfo* info ) * Set the gpu state based on input parameters * Only works when run as root */ -int set_gpu_state( struct GameModeGPUInfo* info ) +int set_gpu_state(struct GameModeGPUInfo *info) { return 0; } diff --git a/daemon/gpu-query.h b/daemon/gpu-query.h index cab5214..5c3eebc 100644 --- a/daemon/gpu-query.h +++ b/daemon/gpu-query.h @@ -52,10 +52,10 @@ struct GameModeGPUInfo { * Get the gpu state * Populates the struct with the GPU info on the system */ -int get_gpu_state( struct GameModeGPUInfo* info ); +int get_gpu_state(struct GameModeGPUInfo *info); /** * Set the gpu state based on input parameters * Only works when run as root */ -int set_gpu_state( struct GameModeGPUInfo* info ); +int set_gpu_state(struct GameModeGPUInfo *info); diff --git a/example/gamemode.ini b/example/gamemode.ini index f366e46..1a2f2a9 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -42,6 +42,10 @@ inhibit_screensaver=1 ; Setting this to 1 will allow gamemode to attempt to apply GPU optimisations such as overclocks ;apply_gpu_optimisations=0 +; Set these to specify which vendor and device ID you want apply optimisations to +;gpu_vendor=0x0000 +;gpu_device=0 + ; Nvidia specific settings (these are Mhz offsets from the baseline, ie. 0 applies no change) ;nv_core_clock_mhz_offset=0 ;nv_mem_clock_mhz_offset=0 From fe9b5c8744f40a46d51adb4b3d04a260fe06c802 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 31 Jan 2019 18:25:46 +0000 Subject: [PATCH 05/42] Error with invalid device or vendor values --- daemon/daemon_config.c | 2 +- daemon/gamemode-gpu.c | 24 ++++++++++++++++++++++-- daemon/gpu-query.h | 4 ++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index f342254..6af386b 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -261,7 +261,7 @@ static void load_config_files(GameModeConfig *self) self->inhibit_screensaver = 1; /* Defaults to on */ self->apply_gpu_optimisations = 0; self->gpu_vendor = 0; - self->gpu_device = 0; + self->gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ self->nv_core_clock_mhz_offset = 0; self->nv_mem_clock_mhz_offset = 0; self->amd_core_clock_percentage = 0; diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index d37e941..01aa519 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -68,12 +68,31 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); memset(new_info, 0, sizeof(GameModeGPUInfo)); - // TODO: Fill in the GPU vendor and device automatically - /* Get the config parameters */ config_get_gpu_vendor(config, &new_info->vendor); config_get_gpu_device(config, &new_info->device); + /* TODO: Detect the GPU vendor and device automatically when these aren't set */ + + /* verify device ID */ + if (new_info->device == -1) { + LOG_ERROR("Invalid gpu_device value set in configuration, will not appli optimisations!\n"); + free(new_info); + return -1; + } + + /* verify GPU vendor */ + if (new_info->vendor != Vendor_NVIDIA && new_info->vendor != Vendor_AMD && + new_info->vendor != Vendor_Intel) { + LOG_ERROR("Invalid gpu_vendor value set in configuration, will not apply optimisations!\n"); + LOG_ERROR("Possible values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", + Vendor_NVIDIA, + Vendor_AMD, + Vendor_Intel); + free(new_info); + return -1; + } + /* Load the config based on GPU */ switch (new_info->vendor) { case Vendor_NVIDIA: @@ -87,6 +106,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) default: break; } + /* TODO : Sanity check these values */ /* Give back the new gpu info */ *info = new_info; diff --git a/daemon/gpu-query.h b/daemon/gpu-query.h index 5c3eebc..4116888 100644 --- a/daemon/gpu-query.h +++ b/daemon/gpu-query.h @@ -41,8 +41,8 @@ enum GPUVendor { /* Storage for GPU info*/ struct GameModeGPUInfo { - enum GPUVendor vendor; - int device; /* path to device, ie. /sys/class/drm/card#/ */ + long vendor; + long device; /* path to device, ie. /sys/class/drm/card#/ */ long core; /* Core clock to apply */ long mem; /* Mem clock to apply */ From ad2c218ab394ac1b7100037753af9a6a4584f3c9 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 31 Jan 2019 18:53:06 +0000 Subject: [PATCH 06/42] Allow setting the device value with a hex string Also stops erroring on 0 value longs --- daemon/daemon_config.c | 26 +++++++++++++++++++++++--- daemon/gamemode-gpu.c | 14 ++++++++------ example/gamemode.ini | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 6af386b..b849cca 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -119,7 +119,7 @@ static bool append_value_to_list(const char *list_name, const char *value, } /* - * Get a positive long value from a string + * Get a long value from a string */ static bool get_long_value(const char *value_name, const char *value, long *output) { @@ -129,7 +129,27 @@ static bool get_long_value(const char *value_name, const char *value, long *outp if (errno == ERANGE) { LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value); return false; - } else if (config_value <= 0 || !(*value != '\0' && end && *end == '\0')) { + } else if (!(*value != '\0' && end && *end == '\0')) { + LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value); + return false; + } else { + *output = config_value; + } + + return true; +} +/* + * Get a long value from a hex string + */ +static bool get_long_value_hex(const char *value_name, const char *value, long *output) +{ + char *end = NULL; + long config_value = strtol(value, &end, 16); + + if (errno == ERANGE) { + LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value); + return false; + } else if (!(*value != '\0' && end && *end == '\0')) { LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value); return false; } else { @@ -186,7 +206,7 @@ static int inih_handler(void *user, const char *section, const char *name, const if (strcmp(name, "apply_gpu_optimisations") == 0) { valid = get_long_value(name, value, &self->apply_gpu_optimisations); } else if (strcmp(name, "gpu_vendor") == 0) { - valid = get_long_value(name, value, &self->gpu_device); + valid = get_long_value_hex(name, value, &self->gpu_vendor); } else if (strcmp(name, "gpu_device") == 0) { valid = get_long_value(name, value, &self->gpu_device); } else if (strcmp(name, "nv_core_clock_mhz_offset") == 0) { diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 01aa519..5385acd 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -40,8 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-query.h" // TODO -// Gather GPU type and information -// Allow override of vendor and device +// Gather GPU type and information automatically // Apply Nvidia GPU settings (CoolBits will be needed) // Apply AMD GPU settings (Will need user changing pwm1_enable) // Intel? @@ -76,15 +75,18 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* verify device ID */ if (new_info->device == -1) { - LOG_ERROR("Invalid gpu_device value set in configuration, will not appli optimisations!\n"); + LOG_ERROR("Invalid gpu_device value set in configuration, will not apply optimisations!\n"); free(new_info); return -1; } /* verify GPU vendor */ - if (new_info->vendor != Vendor_NVIDIA && new_info->vendor != Vendor_AMD && - new_info->vendor != Vendor_Intel) { - LOG_ERROR("Invalid gpu_vendor value set in configuration, will not apply optimisations!\n"); + if (!(new_info->vendor == Vendor_NVIDIA || new_info->vendor == Vendor_AMD || + new_info->vendor == Vendor_Intel)) { + LOG_ERROR( + "Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " + "optimisations!\n", + (unsigned int)new_info->vendor); LOG_ERROR("Possible values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", Vendor_NVIDIA, Vendor_AMD, diff --git a/example/gamemode.ini b/example/gamemode.ini index 1a2f2a9..86f9331 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -43,6 +43,7 @@ inhibit_screensaver=1 ;apply_gpu_optimisations=0 ; Set these to specify which vendor and device ID you want apply optimisations to +; Vendor must be one of 0x10de (NVIDIA), 0x1002 (AMD) or 0x8086 (Intel) ;gpu_vendor=0x0000 ;gpu_device=0 From a9237bc1dde9ad39ab7188a56f00f93430fd2c02 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 15:30:55 +0000 Subject: [PATCH 07/42] Add ERROR to some error messages --- daemon/gamemode-gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 5385acd..c3ce02c 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -75,7 +75,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* verify device ID */ if (new_info->device == -1) { - LOG_ERROR("Invalid gpu_device value set in configuration, will not apply optimisations!\n"); + LOG_ERROR("ERROR: Invalid gpu_device value set in configuration, will not apply optimisations!\n"); free(new_info); return -1; } @@ -84,7 +84,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) if (!(new_info->vendor == Vendor_NVIDIA || new_info->vendor == Vendor_AMD || new_info->vendor == Vendor_Intel)) { LOG_ERROR( - "Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " + "ERROR: Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " "optimisations!\n", (unsigned int)new_info->vendor); LOG_ERROR("Possible values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", From 252aa89fa61ef0b7a8a3b2abc8cfc64fce2654e5 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 15:31:10 +0000 Subject: [PATCH 08/42] Sanity check the config values These values are up for debate, perhaps AMD or NV give nice limits --- daemon/gamemode-gpu.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index c3ce02c..1ffa634 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -95,24 +95,46 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) return -1; } - /* Load the config based on GPU */ + /* Load the config based on GPU and also verify the values are sane */ switch (new_info->vendor) { case Vendor_NVIDIA: config_get_nv_core_clock_mhz_offset(config, &new_info->core); config_get_nv_mem_clock_mhz_offset(config, &new_info->mem); + + /* Reject values over some guessed values + * If a user wants to go into very unsafe levels they can recompile + */ + const int nv_core_hard_limit = 200; + const int nv_mem_hard_limit = 2000; + if( new_info->core > nv_core_hard_limit || new_info->mem > nv_mem_hard_limit ) { + LOG_ERROR("ERROR NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will not overclock!\n", nv_core_hard_limit, nv_mem_hard_limit ); + LOG_ERROR("nv_core_clock_mhz_offset:%ld nv_mem_clock_mhz_offset:%ld\n", new_info->core, new_info->mem ); + free(new_info); + return -1; + } + break; case Vendor_AMD: config_get_amd_core_clock_percentage(config, &new_info->core); config_get_amd_mem_clock_percentage(config, &new_info->mem); + + /* Reject values over 25% + * If a user wants to go into very unsafe levels they can recompile + */ + const int amd_hard_limit = 25; + if( new_info->core > amd_hard_limit || new_info->mem > amd_hard_limit ) { + LOG_ERROR("ERROR AMD Overclock value above safety level of %d%%, will not overclock!\n", amd_hard_limit ); + LOG_ERROR("amd_core_clock_percentage:%ld amd_mem_clock_percentage:%ld\n", new_info->core, new_info->mem ); + free(new_info); + return -1; + } break; default: break; } - /* TODO : Sanity check these values */ /* Give back the new gpu info */ *info = new_info; - return status; } From 7bdbc1adc59adfc0ea6b2a2d6c9cf172ad077f49 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 15:35:31 +0000 Subject: [PATCH 09/42] Add more helper comments to the config ini --- example/gamemode.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/gamemode.ini b/example/gamemode.ini index 86f9331..a5e5da8 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -38,6 +38,7 @@ inhibit_screensaver=1 ; Here Be Dragons! ; Warning: Use these settings at your own risk ; Any damage to hardware incurred due to this feature is your responsibility and yours alone +; It is also highly recommended you try these settings out first manually to find the sweet spots ; Setting this to 1 will allow gamemode to attempt to apply GPU optimisations such as overclocks ;apply_gpu_optimisations=0 @@ -48,10 +49,13 @@ inhibit_screensaver=1 ;gpu_device=0 ; Nvidia specific settings (these are Mhz offsets from the baseline, ie. 0 applies no change) +; Requires the coolbits extension activated in nvidia-xconfig ;nv_core_clock_mhz_offset=0 ;nv_mem_clock_mhz_offset=0 ; AMD specific settings (these are percentages applied on top of the baseline, ie. 0 applies no change) +; Requires the the AMDGPU kernel module +; It is also highly recommended you use lm-sensors (or other available tools) to verify card temperatures ;amd_core_clock_percentage=0 ;amd_mem_clock_percentage=0 From 6b268e8349956ed7a0bc4a4d4efda316d604d211 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 15:54:29 +0000 Subject: [PATCH 10/42] Convert "apply_gpu_optimisations" to a string with a special key --- daemon/daemon_config.c | 15 +++++++++------ daemon/daemon_config.h | 2 +- daemon/gamemode-gpu.c | 13 ++++++++++--- example/gamemode.ini | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index b849cca..371792b 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -76,7 +76,7 @@ struct GameModeConfig { long reaper_frequency; - long apply_gpu_optimisations; + char apply_gpu_optimisations[CONFIG_VALUE_MAX]; long gpu_vendor; long gpu_device; long nv_core_clock_mhz_offset; @@ -204,7 +204,7 @@ static int inih_handler(void *user, const char *section, const char *name, const } else if (strcmp(section, "gpu") == 0) { /* GPU subsection */ if (strcmp(name, "apply_gpu_optimisations") == 0) { - valid = get_long_value(name, value, &self->apply_gpu_optimisations); + valid = get_string_value(value, self->apply_gpu_optimisations); } else if (strcmp(name, "gpu_vendor") == 0) { valid = get_long_value_hex(name, value, &self->gpu_vendor); } else if (strcmp(name, "gpu_device") == 0) { @@ -276,10 +276,10 @@ static void load_config_files(GameModeConfig *self) memset(self->defaultgov, 0, sizeof(self->defaultgov)); memset(self->desiredgov, 0, sizeof(self->desiredgov)); memset(self->softrealtime, 0, sizeof(self->softrealtime)); + memset(self->apply_gpu_optimisations, 0, sizeof(self->apply_gpu_optimisations)); + self->inhibit_screensaver = 1; /* Defaults to on */ self->renice = 4; /* default value of 4 */ self->reaper_frequency = DEFAULT_REAPER_FREQ; - self->inhibit_screensaver = 1; /* Defaults to on */ - self->apply_gpu_optimisations = 0; self->gpu_vendor = 0; self->gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ self->nv_core_clock_mhz_offset = 0; @@ -517,9 +517,12 @@ void config_get_ioprio_value(GameModeConfig *self, int *value) /* * Get various config info for gpu optimisations */ -void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value) +void config_get_apply_gpu_optimisations(GameModeConfig *self, char value[CONFIG_VALUE_MAX]) { - memcpy_locked_config(self, value, &self->apply_gpu_optimisations, sizeof(long)); + memcpy_locked_config(self, + value, + &self->apply_gpu_optimisations, + sizeof(self->apply_gpu_optimisations)); } void config_get_gpu_vendor(GameModeConfig *self, long *value) diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index 21c9fd0..e4aed7d 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -133,7 +133,7 @@ void config_get_ioprio_value(GameModeConfig *self, int *value); /* * Get various config info for gpu optimisations */ -void config_get_apply_gpu_optimisations(GameModeConfig *self, long *value); +void config_get_apply_gpu_optimisations(GameModeConfig *self, char value[CONFIG_VALUE_MAX]); void config_get_gpu_vendor(GameModeConfig *self, long *value); void config_get_gpu_device(GameModeConfig *self, long *value); void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 1ffa634..b705dc1 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -58,10 +58,17 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) FATAL_ERROR("Invalid GameModeGPUInfo passed to %s", __func__); /* Early out if we have this feature turned off */ - long apply = 0; - config_get_apply_gpu_optimisations(config, &apply); - if (apply == 0) + char apply[CONFIG_VALUE_MAX]; + config_get_apply_gpu_optimisations(config, apply); + if (strlen(apply) == 0) { return 0; + } else if (strncmp(apply, "accept-responsibility", CONFIG_VALUE_MAX) != 0) { + LOG_ERROR( + "apply_gpu_optimisations set to value other than \"accept-responsibility\" (%s), will " + "not apply GPU optimisations!\n", + apply); + return -1; + } /* Create the context */ GameModeGPUInfo *new_info = malloc(sizeof(GameModeGPUInfo)); diff --git a/example/gamemode.ini b/example/gamemode.ini index a5e5da8..f09de7c 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -40,7 +40,7 @@ inhibit_screensaver=1 ; Any damage to hardware incurred due to this feature is your responsibility and yours alone ; It is also highly recommended you try these settings out first manually to find the sweet spots -; Setting this to 1 will allow gamemode to attempt to apply GPU optimisations such as overclocks +; Setting this to the keyphrase "accept-responsibility" will allow gamemode to apply GPU optimisations such as overclocks ;apply_gpu_optimisations=0 ; Set these to specify which vendor and device ID you want apply optimisations to From 0f6c9a8a95ec64b04cdd03e09a0aba793894426b Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 15:54:48 +0000 Subject: [PATCH 11/42] Fix formatting --- daemon/gamemode-gpu.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index b705dc1..223128b 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -82,7 +82,9 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* verify device ID */ if (new_info->device == -1) { - LOG_ERROR("ERROR: Invalid gpu_device value set in configuration, will not apply optimisations!\n"); + LOG_ERROR( + "ERROR: Invalid gpu_device value set in configuration, will not apply " + "optimisations!\n"); free(new_info); return -1; } @@ -110,12 +112,18 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* Reject values over some guessed values * If a user wants to go into very unsafe levels they can recompile - */ + */ const int nv_core_hard_limit = 200; const int nv_mem_hard_limit = 2000; - if( new_info->core > nv_core_hard_limit || new_info->mem > nv_mem_hard_limit ) { - LOG_ERROR("ERROR NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will not overclock!\n", nv_core_hard_limit, nv_mem_hard_limit ); - LOG_ERROR("nv_core_clock_mhz_offset:%ld nv_mem_clock_mhz_offset:%ld\n", new_info->core, new_info->mem ); + if (new_info->core > nv_core_hard_limit || new_info->mem > nv_mem_hard_limit) { + LOG_ERROR( + "ERROR NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will " + "not overclock!\n", + nv_core_hard_limit, + nv_mem_hard_limit); + LOG_ERROR("nv_core_clock_mhz_offset:%ld nv_mem_clock_mhz_offset:%ld\n", + new_info->core, + new_info->mem); free(new_info); return -1; } @@ -127,11 +135,14 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* Reject values over 25% * If a user wants to go into very unsafe levels they can recompile - */ + */ const int amd_hard_limit = 25; - if( new_info->core > amd_hard_limit || new_info->mem > amd_hard_limit ) { - LOG_ERROR("ERROR AMD Overclock value above safety level of %d%%, will not overclock!\n", amd_hard_limit ); - LOG_ERROR("amd_core_clock_percentage:%ld amd_mem_clock_percentage:%ld\n", new_info->core, new_info->mem ); + if (new_info->core > amd_hard_limit || new_info->mem > amd_hard_limit) { + LOG_ERROR("ERROR AMD Overclock value above safety level of %d%%, will not overclock!\n", + amd_hard_limit); + LOG_ERROR("amd_core_clock_percentage:%ld amd_mem_clock_percentage:%ld\n", + new_info->core, + new_info->mem); free(new_info); return -1; } From a395caeb48e020c903a803b0c7d7746b3d27870e Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 16:18:59 +0000 Subject: [PATCH 12/42] Refactor the governor request into an external process helper function --- daemon/dbus_messaging.c | 1 - daemon/{governors.c => external-helper.c} | 22 ++++--------- daemon/{governors.h => external-helper.h} | 9 ++--- daemon/gamemode-gpu.c | 40 ++++++++++++++++++++++- daemon/gamemode.c | 22 ++++++++++--- daemon/meson.build | 2 +- 6 files changed, 67 insertions(+), 29 deletions(-) rename daemon/{governors.c => external-helper.c} (84%) rename daemon/{governors.h => external-helper.h} (90%) diff --git a/daemon/dbus_messaging.c b/daemon/dbus_messaging.c index c411e0a..f777612 100644 --- a/daemon/dbus_messaging.c +++ b/daemon/dbus_messaging.c @@ -34,7 +34,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "dbus_messaging.h" #include "daemonize.h" #include "gamemode.h" -#include "governors.h" #include "logging.h" #include diff --git a/daemon/governors.c b/daemon/external-helper.c similarity index 84% rename from daemon/governors.c rename to daemon/external-helper.c index 9836491..096317e 100644 --- a/daemon/governors.c +++ b/daemon/external-helper.c @@ -31,9 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE -#include "governors.h" #include "config.h" -#include "governors-query.h" #include "logging.h" #include @@ -42,21 +40,15 @@ POSSIBILITY OF SUCH DAMAGE. #include /** - * Update the governors to the given argument, via pkexec + * Call an external process */ -bool set_governors(const char *value) +int run_external_process(const char *const *exec_args) { pid_t p; int status = 0; int ret = 0; int r = -1; - const char *const exec_args[] = { - "/usr/bin/pkexec", LIBEXECDIR "/cpugovctl", "set", value, NULL, - }; - - LOG_MSG("Requesting update of governor policy to %s\n", value); - if ((p = fork()) < 0) { LOG_ERROR("Failed to fork(): %s\n", strerror(errno)); return false; @@ -69,14 +61,14 @@ bool set_governors(const char *value) * http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html */ if ((r = execv(exec_args[0], (char *const *)exec_args)) != 0) { - LOG_ERROR("Failed to execute cpugovctl helper: %s %s\n", exec_args[1], strerror(errno)); + LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[1], strerror(errno)); exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } else { if (waitpid(p, &status, 0) < 0) { LOG_ERROR("Failed to waitpid(%d): %s\n", (int)p, strerror(errno)); - return false; + return -1; } /* i.e. sigsev */ if (!WIFEXITED(status)) { @@ -85,9 +77,9 @@ bool set_governors(const char *value) } if ((ret = WEXITSTATUS(status)) != 0) { - LOG_ERROR("Failed to update cpu governor policy\n"); - return false; + LOG_ERROR("External process failed\n"); + return -1; } - return true; + return 0; } diff --git a/daemon/governors.h b/daemon/external-helper.h similarity index 90% rename from daemon/governors.h rename to daemon/external-helper.h index f382f8f..9d51db8 100644 --- a/daemon/governors.h +++ b/daemon/external-helper.h @@ -31,10 +31,5 @@ POSSIBILITY OF SUCH DAMAGE. #pragma once -#include - -/** - * Update all governors to the given value. If this is NULL, restore the - * initial governor. - */ -bool set_governors(const char *value); +/* Run an external process and capture the return value */ +int run_external_process(const char *const *exec_args); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 223128b..f339d40 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -164,6 +164,11 @@ void game_mode_free_gpu(GameModeGPUInfo **info) *info = NULL; } +//#include +//#include +//#include +//#include + /** * Applies GPU optimisations when gamemode is active and removes them after */ @@ -173,7 +178,40 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) if (!info) return 0; - /* TODO Call gpuclockctl set */ + /* + pid_t p; + int status = 0; + int ret = 0; + int r = -1; + + const char *const exec_args[] = { + "/usr/bin/pkexec", LIBEXECDIR "/gpuclockctl", "set", value, NULL, + }; + + LOG_MSG("Requesting new GPU settings %s\n", value); + + if ((p = fork()) < 0) { + LOG_ERROR("Failed to fork(): %s\n", strerror(errno)); + return false; + } else if (p == 0) { + if ((r = execv(exec_args[0], (char *const *)exec_args)) != 0) { + LOG_ERROR("Failed to execute cpugovctl helper: %s %s\n", exec_args[1], + strerror(errno)); exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); + } else { + if (waitpid(p, &status, 0) < 0) { + LOG_ERROR("Failed to waitpid(%d): %s\n", (int)p, strerror(errno)); + return false; + } + if (!WIFEXITED(status)) { + LOG_ERROR("Child process '%s' exited abnormally\n", exec_args[0]); + } + } + + if ((ret = WEXITSTATUS(status)) != 0) { + LOG_ERROR("Failed to update cpu governor policy\n"); + */ return 0; } diff --git a/daemon/gamemode.c b/daemon/gamemode.c index 2b80057..bdfdae9 100644 --- a/daemon/gamemode.c +++ b/daemon/gamemode.c @@ -32,10 +32,11 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE #include "gamemode.h" +#include "config.h" #include "daemon_config.h" #include "dbus_messaging.h" +#include "external-helper.h" #include "governors-query.h" -#include "governors.h" #include "helpers.h" #include "logging.h" @@ -197,8 +198,13 @@ static void game_mode_context_enter(GameModeContext *self) config_get_desired_governor(self->config, desired); const char *desiredGov = desired[0] != '\0' ? desired : "performance"; - /* set the governor to performance */ - if (!set_governors(desiredGov)) { + const char *const exec_args[] = { + "/usr/bin/pkexec", LIBEXECDIR "/cpugovctl", "set", desiredGov, NULL, + }; + + LOG_MSG("Requesting update of governor policy to %s\n", desiredGov); + if (run_external_process(exec_args) != 0) { + LOG_ERROR("Failed to update cpu governor policy\n"); /* if the set fails, clear the initial mode so we don't try and reset it back and fail * again, presumably */ memset(self->initial_cpu_mode, 0, sizeof(self->initial_cpu_mode)); @@ -238,7 +244,15 @@ static void game_mode_context_leave(GameModeContext *self) config_get_default_governor(self->config, defaultgov); const char *gov_mode = defaultgov[0] != '\0' ? defaultgov : self->initial_cpu_mode; - set_governors(gov_mode); + const char *const exec_args[] = { + "/usr/bin/pkexec", LIBEXECDIR "/cpugovctl", "set", gov_mode, NULL, + }; + + LOG_MSG("Requesting update of governor policy to %s\n", gov_mode); + if (run_external_process(exec_args) != 0) { + LOG_ERROR("Failed to update cpu governor policy\n"); + } + memset(self->initial_cpu_mode, 0, sizeof(self->initial_cpu_mode)); } diff --git a/daemon/meson.build b/daemon/meson.build index 9b79453..46e40a4 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -28,7 +28,7 @@ daemon_sources = [ 'gamemode-gpu.c', 'daemonize.c', 'dbus_messaging.c', - 'governors.c', + 'external-helper.c', 'daemon_config.c', ] From 8a52e812d904786dd5eba4e01bd8fb76c5167e24 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 16:27:27 +0000 Subject: [PATCH 13/42] Correct format args --- daemon/gpuclockctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index f954815..7730ca6 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) get_gpu_state(&info); - printf("%d %d\n", info.core, info.mem); + printf("%ld %ld\n", info.core, info.mem); } else if (argc == 6 && strncmp(argv[3], "set", 3) == 0) { const char *vendor = argv[1]; From 57a55c41cc607d47686da65455b54342dd6a808d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 16:36:06 +0000 Subject: [PATCH 14/42] Call gpuclockctl --- daemon/gamemode-gpu.c | 48 +++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index f339d40..d865a1d 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -35,6 +35,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "gamemode.h" #include "helpers.h" #include "logging.h" +#include "config.h" +#include "external-helper.h" #include "daemon_config.h" #include "gpu-query.h" @@ -178,40 +180,22 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) if (!info) return 0; - /* - pid_t p; - int status = 0; - int ret = 0; - int r = -1; + LOG_MSG("Requesting GPU optimisations on device:%ld with settings core:%ld clock:%ld\n", + info->device, + info->core, + info->mem); - const char *const exec_args[] = { - "/usr/bin/pkexec", LIBEXECDIR "/gpuclockctl", "set", value, NULL, - }; + // TODO: Actually pass right arguments + const char *const exec_args[] = { + "/usr/bin/pkexec", + LIBEXECDIR "/gpuclockctl", + NULL, + }; - LOG_MSG("Requesting new GPU settings %s\n", value); - - if ((p = fork()) < 0) { - LOG_ERROR("Failed to fork(): %s\n", strerror(errno)); - return false; - } else if (p == 0) { - if ((r = execv(exec_args[0], (char *const *)exec_args)) != 0) { - LOG_ERROR("Failed to execute cpugovctl helper: %s %s\n", exec_args[1], - strerror(errno)); exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } else { - if (waitpid(p, &status, 0) < 0) { - LOG_ERROR("Failed to waitpid(%d): %s\n", (int)p, strerror(errno)); - return false; - } - if (!WIFEXITED(status)) { - LOG_ERROR("Child process '%s' exited abnormally\n", exec_args[0]); - } - } - - if ((ret = WEXITSTATUS(status)) != 0) { - LOG_ERROR("Failed to update cpu governor policy\n"); - */ + if (run_external_process(exec_args) != 0) { + LOG_ERROR("ERROR: Failed to call gpuclockctl, could not apply optimisations!\n"); + return -1; + } return 0; } From fa38ef4e237dcdde1bf6b2cb6437099ce147415a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 17:26:42 +0000 Subject: [PATCH 15/42] Add a macro to validate a vendor value --- daemon/gamemode-gpu.c | 3 +-- daemon/gpu-query.h | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index d865a1d..53229ad 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -92,8 +92,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) } /* verify GPU vendor */ - if (!(new_info->vendor == Vendor_NVIDIA || new_info->vendor == Vendor_AMD || - new_info->vendor == Vendor_Intel)) { + if (!GPUVendorValid(new_info->vendor)) { LOG_ERROR( "ERROR: Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " "optimisations!\n", diff --git a/daemon/gpu-query.h b/daemon/gpu-query.h index 4116888..77f2d86 100644 --- a/daemon/gpu-query.h +++ b/daemon/gpu-query.h @@ -39,6 +39,9 @@ enum GPUVendor { Vendor_Intel = 0x8086 }; +#define GPUVendorValid(vendor) \ + (vendor == Vendor_NVIDIA || vendor == Vendor_AMD || vendor == Vendor_Intel) + /* Storage for GPU info*/ struct GameModeGPUInfo { long vendor; From 198490854393976e1239e85b228bbb4e79a65e83 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 17:26:57 +0000 Subject: [PATCH 16/42] Fix include order --- daemon/gamemode-gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 53229ad..c80b178 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -32,11 +32,11 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE +#include "config.h" +#include "external-helper.h" #include "gamemode.h" #include "helpers.h" #include "logging.h" -#include "config.h" -#include "external-helper.h" #include "daemon_config.h" #include "gpu-query.h" From a1a1829dea0c59d5ec1c3cc1a39f089299520de4 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 17:27:06 +0000 Subject: [PATCH 17/42] Add argument parsing and errors to gpuclockctl --- daemon/gpuclockctl.c | 75 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 7730ca6..e4eced8 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -35,41 +35,84 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-query.h" +/* Helper to quit with usage */ +static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM]"; +static void print_usage_and_exit(void) +{ + fprintf(stderr, "%s\n", usage_text); + exit(EXIT_FAILURE); +} + +/* Helper to get and verify vendor value */ +static long get_vendor(const char *val) +{ + char *end; + long ret = strtol(val, &end, 0); + if (!GPUVendorValid(ret) || end == val) { + LOG_ERROR("ERROR: Invalid GPU Vendor passed (0x%04x)!\n", (unsigned short)ret); + print_usage_and_exit(); + } + return ret; +} + +/* Helper to get and verify device value */ +static long get_device(const char *val) +{ + char *end; + long ret = strtol(val, &end, 10); + if (ret < 0 || end == val) { + LOG_ERROR("ERROR: Invalid GPU device passed (%ld)!\n", ret); + print_usage_and_exit(); + } + return ret; +} + +/* Helper to get and verify core and mem value */ +static long get_coremem(const char *val) +{ + char *end; + long ret = strtol(val, &end, 10); + if (ret < 0 || end == val) { + LOG_ERROR("ERROR: Invalid core or mem value passed (%ld)!\n", ret); + print_usage_and_exit(); + } + return ret; +} + /** * Main entry point, dispatch to the appropriate helper */ int main(int argc, char *argv[]) { - if (argc == 4 && strncmp(argv[1], "get", 3) == 0) { - const char *vendor = argv[1]; - const char *device = argv[2]; - + if (argc == 4 && strncmp(argv[3], "get", 3) == 0) { + /* Get and verify the vendor and device */ struct GameModeGPUInfo info; - /* TODO Populate with vendor and device */ + memset(&info, 0, sizeof(info)); + info.vendor = get_vendor(argv[1]); + info.device = get_device(argv[2]); + /* Fetch the state and print it out */ get_gpu_state(&info); - printf("%ld %ld\n", info.core, info.mem); } else if (argc == 6 && strncmp(argv[3], "set", 3) == 0) { - const char *vendor = argv[1]; - const char *device = argv[2]; - const char *core = argv[4]; - const char *mem = argv[5]; - /* Must be root to set the state */ if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root\n"); - return EXIT_FAILURE; + fprintf(stderr, "gpuclockctl must be run as root to set values\n"); + print_usage_and_exit(); } + /* Get and verify the vendor and device */ struct GameModeGPUInfo info; - /* TODO Populate with vendor, device and clocks */ + memset(&info, 0, sizeof(info)); + info.vendor = get_vendor(argv[1]); + info.device = get_device(argv[2]); + info.core = get_coremem(argv[4]); + info.mem = get_coremem(argv[4]); return set_gpu_state(&info); } else { - fprintf(stderr, "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM]\n"); - return EXIT_FAILURE; + print_usage_and_exit(); } return EXIT_SUCCESS; From 01dbe4e2b06bdf9ab1f2a8b67b97fb28b7d97426 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 17:35:21 +0000 Subject: [PATCH 18/42] Rename gpu-query to gpu-control to better describe it's functions --- daemon/gamemode-gpu.c | 2 +- daemon/{gpu-query.c => gpu-control.c} | 2 +- daemon/{gpu-query.h => gpu-control.h} | 0 daemon/gpuclockctl.c | 2 +- daemon/meson.build | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename daemon/{gpu-query.c => gpu-control.c} (98%) rename daemon/{gpu-query.h => gpu-control.h} (100%) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index c80b178..db3a9d1 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -39,7 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "logging.h" #include "daemon_config.h" -#include "gpu-query.h" +#include "gpu-control.h" // TODO // Gather GPU type and information automatically diff --git a/daemon/gpu-query.c b/daemon/gpu-control.c similarity index 98% rename from daemon/gpu-query.c rename to daemon/gpu-control.c index 0f6b2e2..491db39 100644 --- a/daemon/gpu-query.c +++ b/daemon/gpu-control.c @@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE -#include "gpu-query.h" +#include "gpu-control.h" #include "logging.h" // NVIDIA diff --git a/daemon/gpu-query.h b/daemon/gpu-control.h similarity index 100% rename from daemon/gpu-query.h rename to daemon/gpu-control.h diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index e4eced8..cb32baf 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -33,7 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "logging.h" -#include "gpu-query.h" +#include "gpu-control.h" /* Helper to quit with usage */ static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM]"; diff --git a/daemon/meson.build b/daemon/meson.build index 46e40a4..1c57061 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -2,7 +2,7 @@ common_sources = [ 'logging.c', 'governors-query.c', - 'gpu-query.c', + 'gpu-control.c', ] daemon_common = static_library( From 8ea751057c625a5b58ad5e4dee562843a2d0f249 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 17:36:08 +0000 Subject: [PATCH 19/42] Move the TODO into the control file --- daemon/gamemode-gpu.c | 7 ------- daemon/gpu-control.c | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index db3a9d1..c0108c7 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -41,13 +41,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "daemon_config.h" #include "gpu-control.h" -// TODO -// Gather GPU type and information automatically -// Apply Nvidia GPU settings (CoolBits will be needed) -// Apply AMD GPU settings (Will need user changing pwm1_enable) -// Intel? -// Provide documentation on optimisations - /** * Attempts to identify the current in use GPU information */ diff --git a/daemon/gpu-control.c b/daemon/gpu-control.c index 491db39..6b24f65 100644 --- a/daemon/gpu-control.c +++ b/daemon/gpu-control.c @@ -34,6 +34,13 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-control.h" #include "logging.h" +// TODO +// Gather GPU type and information automatically +// Apply Nvidia GPU settings (CoolBits will be needed) +// Apply AMD GPU settings (Will need user changing pwm1_enable) +// Intel? +// Provide documentation on optimisations + // NVIDIA // Running these commands: // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' From b85edc2e044267796800e026103c1e39e0922a98 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:07:45 +0000 Subject: [PATCH 20/42] Hand down correct set parameters to gpucorectl --- daemon/gamemode-gpu.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index c0108c7..5f5d523 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -177,10 +177,25 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) info->core, info->mem); + /* Generate the input strings */ + char vendor[7]; + snprintf(vendor, 7, "0x%04x", (short)info->vendor); + char device[4]; + snprintf(device, 4, "%ld", info->device); + char core[8]; + snprintf(core, 8, "%ld", info->core); + char mem[8]; + snprintf(mem, 8, "%ld", info->mem); + // TODO: Actually pass right arguments const char *const exec_args[] = { "/usr/bin/pkexec", LIBEXECDIR "/gpuclockctl", + vendor, + device, + "set", + apply ? core : "0", /* For now simply reset to zero */ + apply ? mem : "0", /* could in the future store default values for reset */ NULL, }; From 4152104d2bae28d617e49f1fa032d4e9065f5ac3 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:08:46 +0000 Subject: [PATCH 21/42] Correct the mem argv index and print the intended change --- daemon/gpuclockctl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index cb32baf..aec086c 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -108,7 +108,13 @@ int main(int argc, char *argv[]) info.vendor = get_vendor(argv[1]); info.device = get_device(argv[2]); info.core = get_coremem(argv[4]); - info.mem = get_coremem(argv[4]); + info.mem = get_coremem(argv[5]); + + printf("gpuclockctl setting core:%ld mem:%ld on device:%ld with vendor 0x%04x\n", + info.core, + info.mem, + info.device, + (unsigned short)info.vendor); return set_gpu_state(&info); } else { From 142b2fb32d978ceed0e1352bb263581d44d32d59 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:33:41 +0000 Subject: [PATCH 22/42] Also add nv_perf_level for nvidia (needed as a parameter to nvidia-xsettings) --- daemon/daemon_config.c | 9 +++++++++ daemon/daemon_config.h | 1 + daemon/gamemode-gpu.c | 14 +++++++++++++- daemon/gpu-control.c | 4 ++-- daemon/gpu-control.h | 2 ++ daemon/gpuclockctl.c | 18 ++++++++++++------ example/gamemode.ini | 2 ++ 7 files changed, 41 insertions(+), 9 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 371792b..feedde7 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -81,6 +81,7 @@ struct GameModeConfig { long gpu_device; long nv_core_clock_mhz_offset; long nv_mem_clock_mhz_offset; + long nv_perf_level; long amd_core_clock_percentage; long amd_mem_clock_percentage; }; @@ -213,6 +214,8 @@ static int inih_handler(void *user, const char *section, const char *name, const valid = get_long_value(name, value, &self->nv_core_clock_mhz_offset); } else if (strcmp(name, "nv_mem_clock_mhz_offset") == 0) { valid = get_long_value(name, value, &self->nv_mem_clock_mhz_offset); + } else if (strcmp(name, "nv_perf_level") == 0) { + valid = get_long_value(name, value, &self->nv_perf_level); } else if (strcmp(name, "amd_core_clock_percentage") == 0) { valid = get_long_value(name, value, &self->amd_core_clock_percentage); } else if (strcmp(name, "amd_mem_clock_percentage") == 0) { @@ -284,6 +287,7 @@ static void load_config_files(GameModeConfig *self) self->gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ self->nv_core_clock_mhz_offset = 0; self->nv_mem_clock_mhz_offset = 0; + self->nv_perf_level = -1; self->amd_core_clock_percentage = 0; self->amd_mem_clock_percentage = 0; @@ -543,6 +547,11 @@ void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value) void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value) { memcpy_locked_config(self, value, &self->nv_mem_clock_mhz_offset, sizeof(long)); + +} +void config_get_nv_perf_level(GameModeConfig *self, long *value) +{ + memcpy_locked_config(self, value, &self->nv_perf_level, sizeof(long)); } void config_get_amd_core_clock_percentage(GameModeConfig *self, long *value) diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index e4aed7d..f3fcde8 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -138,5 +138,6 @@ void config_get_gpu_vendor(GameModeConfig *self, long *value); void config_get_gpu_device(GameModeConfig *self, long *value); void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value); void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value); +void config_get_nv_perf_level(GameModeConfig *self, long *value); void config_get_amd_core_clock_percentage(GameModeConfig *self, long *value); void config_get_amd_mem_clock_percentage(GameModeConfig *self, long *value); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 5f5d523..c3b6a7d 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -111,7 +111,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) const int nv_mem_hard_limit = 2000; if (new_info->core > nv_core_hard_limit || new_info->mem > nv_mem_hard_limit) { LOG_ERROR( - "ERROR NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will " + "ERROR: NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will " "not overclock!\n", nv_core_hard_limit, nv_mem_hard_limit); @@ -122,6 +122,15 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) return -1; } + /* Sanity check the performance level value as well */ + config_get_nv_perf_level(config, &new_info->nv_perf_level); + if(new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) + { + LOG_ERROR( "ERROR: NVIDIA Performance level value invalid (%ld), will not apply optimisations!\n", new_info->nv_perf_level ); + free(new_info); + return -1; + } + break; case Vendor_AMD: config_get_amd_core_clock_percentage(config, &new_info->core); @@ -186,6 +195,8 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) snprintf(core, 8, "%ld", info->core); char mem[8]; snprintf(mem, 8, "%ld", info->mem); + char nv_perf_level[4]; + snprintf(nv_perf_level, 4, "%ld", info->nv_perf_level); // TODO: Actually pass right arguments const char *const exec_args[] = { @@ -196,6 +207,7 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) "set", apply ? core : "0", /* For now simply reset to zero */ apply ? mem : "0", /* could in the future store default values for reset */ + info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ NULL, }; diff --git a/daemon/gpu-control.c b/daemon/gpu-control.c index 6b24f65..69b38a4 100644 --- a/daemon/gpu-control.c +++ b/daemon/gpu-control.c @@ -35,11 +35,11 @@ POSSIBILITY OF SUCH DAMAGE. #include "logging.h" // TODO -// Gather GPU type and information automatically // Apply Nvidia GPU settings (CoolBits will be needed) // Apply AMD GPU settings (Will need user changing pwm1_enable) -// Intel? // Provide documentation on optimisations +// Intel? +// Gather GPU type and information automatically if possible // NVIDIA // Running these commands: diff --git a/daemon/gpu-control.h b/daemon/gpu-control.h index 77f2d86..239fffa 100644 --- a/daemon/gpu-control.h +++ b/daemon/gpu-control.h @@ -49,6 +49,8 @@ struct GameModeGPUInfo { long core; /* Core clock to apply */ long mem; /* Mem clock to apply */ + + long nv_perf_level; /* The Nvidia Performance Level to adjust */ }; /** diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index aec086c..026bd1b 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -36,7 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-control.h" /* Helper to quit with usage */ -static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM]"; +static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; static void print_usage_and_exit(void) { fprintf(stderr, "%s\n", usage_text); @@ -68,12 +68,12 @@ static long get_device(const char *val) } /* Helper to get and verify core and mem value */ -static long get_coremem(const char *val) +static long get_generic_value(const char *val) { char *end; long ret = strtol(val, &end, 10); if (ret < 0 || end == val) { - LOG_ERROR("ERROR: Invalid core or mem value passed (%ld)!\n", ret); + LOG_ERROR("ERROR: Invalid value passed (%ld)!\n", ret); print_usage_and_exit(); } return ret; @@ -95,7 +95,7 @@ int main(int argc, char *argv[]) get_gpu_state(&info); printf("%ld %ld\n", info.core, info.mem); - } else if (argc == 6 && strncmp(argv[3], "set", 3) == 0) { + } else if (argc >=6 && argc <=7 && strncmp(argv[3], "set", 3) == 0) { /* Must be root to set the state */ if (geteuid() != 0) { fprintf(stderr, "gpuclockctl must be run as root to set values\n"); @@ -107,8 +107,11 @@ int main(int argc, char *argv[]) memset(&info, 0, sizeof(info)); info.vendor = get_vendor(argv[1]); info.device = get_device(argv[2]); - info.core = get_coremem(argv[4]); - info.mem = get_coremem(argv[5]); + info.core = get_generic_value(argv[4]); + info.mem = get_generic_value(argv[5]); + + if( info.vendor == Vendor_NVIDIA ) + info.nv_perf_level = get_generic_value(argv[6]); printf("gpuclockctl setting core:%ld mem:%ld on device:%ld with vendor 0x%04x\n", info.core, @@ -116,6 +119,9 @@ int main(int argc, char *argv[]) info.device, (unsigned short)info.vendor); + if( info.vendor == Vendor_NVIDIA ) + printf("on Performance Level %ld\n", info.nv_perf_level); + return set_gpu_state(&info); } else { print_usage_and_exit(); diff --git a/example/gamemode.ini b/example/gamemode.ini index f09de7c..d9fb48e 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -52,6 +52,8 @@ inhibit_screensaver=1 ; Requires the coolbits extension activated in nvidia-xconfig ;nv_core_clock_mhz_offset=0 ;nv_mem_clock_mhz_offset=0 +; This corresponds to the performance level to edit in nvidia-xconfig +;nv_perf_level=1 ; AMD specific settings (these are percentages applied on top of the baseline, ie. 0 applies no change) ; Requires the the AMDGPU kernel module From ff1a838ab75635bcb027de72d4bf9c976a23d4ff Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:37:17 +0000 Subject: [PATCH 23/42] Apply format corrections --- daemon/daemon_config.c | 1 - daemon/gamemode-gpu.c | 8 +++++--- daemon/gpuclockctl.c | 9 +++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index feedde7..69d0b0a 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -547,7 +547,6 @@ void config_get_nv_core_clock_mhz_offset(GameModeConfig *self, long *value) void config_get_nv_mem_clock_mhz_offset(GameModeConfig *self, long *value) { memcpy_locked_config(self, value, &self->nv_mem_clock_mhz_offset, sizeof(long)); - } void config_get_nv_perf_level(GameModeConfig *self, long *value) { diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index c3b6a7d..7bc1b80 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -124,9 +124,11 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* Sanity check the performance level value as well */ config_get_nv_perf_level(config, &new_info->nv_perf_level); - if(new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) - { - LOG_ERROR( "ERROR: NVIDIA Performance level value invalid (%ld), will not apply optimisations!\n", new_info->nv_perf_level ); + if (new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) { + LOG_ERROR( + "ERROR: NVIDIA Performance level value invalid (%ld), will not apply " + "optimisations!\n", + new_info->nv_perf_level); free(new_info); return -1; } diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 026bd1b..a892198 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -36,7 +36,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-control.h" /* Helper to quit with usage */ -static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; +static const char *usage_text = + "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; static void print_usage_and_exit(void) { fprintf(stderr, "%s\n", usage_text); @@ -95,7 +96,7 @@ int main(int argc, char *argv[]) get_gpu_state(&info); printf("%ld %ld\n", info.core, info.mem); - } else if (argc >=6 && argc <=7 && strncmp(argv[3], "set", 3) == 0) { + } else if (argc >= 6 && argc <= 7 && strncmp(argv[3], "set", 3) == 0) { /* Must be root to set the state */ if (geteuid() != 0) { fprintf(stderr, "gpuclockctl must be run as root to set values\n"); @@ -110,7 +111,7 @@ int main(int argc, char *argv[]) info.core = get_generic_value(argv[4]); info.mem = get_generic_value(argv[5]); - if( info.vendor == Vendor_NVIDIA ) + if (info.vendor == Vendor_NVIDIA) info.nv_perf_level = get_generic_value(argv[6]); printf("gpuclockctl setting core:%ld mem:%ld on device:%ld with vendor 0x%04x\n", @@ -119,7 +120,7 @@ int main(int argc, char *argv[]) info.device, (unsigned short)info.vendor); - if( info.vendor == Vendor_NVIDIA ) + if (info.vendor == Vendor_NVIDIA) printf("on Performance Level %ld\n", info.nv_perf_level); return set_gpu_state(&info); From 53428356a5366b47bc36e45f3fd7abddf428e7b1 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:41:15 +0000 Subject: [PATCH 24/42] Move control code back into the helper --- daemon/gpu-control.c | 71 -------------------------------------------- daemon/gpu-control.h | 12 -------- daemon/gpuclockctl.c | 48 ++++++++++++++++++++++++++++++ daemon/meson.build | 1 - 4 files changed, 48 insertions(+), 84 deletions(-) delete mode 100644 daemon/gpu-control.c diff --git a/daemon/gpu-control.c b/daemon/gpu-control.c deleted file mode 100644 index 69b38a4..0000000 --- a/daemon/gpu-control.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - -Copyright (c) 2017-2018, Feral Interactive -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 "gpu-control.h" -#include "logging.h" - -// TODO -// Apply Nvidia GPU settings (CoolBits will be needed) -// Apply AMD GPU settings (Will need user changing pwm1_enable) -// Provide documentation on optimisations -// Intel? -// Gather GPU type and information automatically if possible - -// NVIDIA -// Running these commands: -// nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' -// nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' - -// AMD -// We'll want to set both the following: -// core: device/pp_sclk_od (0%+ additional) -// mem: device/pp_mclk_od (0%+ additional) -// Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ - -/** - * Get the gpu state - * Populates the struct with the GPU info on the system - */ -int get_gpu_state(struct GameModeGPUInfo *info) -{ - return 0; -} - -/** - * Set the gpu state based on input parameters - * Only works when run as root - */ -int set_gpu_state(struct GameModeGPUInfo *info) -{ - return 0; -} diff --git a/daemon/gpu-control.h b/daemon/gpu-control.h index 239fffa..c4b611b 100644 --- a/daemon/gpu-control.h +++ b/daemon/gpu-control.h @@ -52,15 +52,3 @@ struct GameModeGPUInfo { long nv_perf_level; /* The Nvidia Performance Level to adjust */ }; - -/** - * Get the gpu state - * Populates the struct with the GPU info on the system - */ -int get_gpu_state(struct GameModeGPUInfo *info); - -/** - * Set the gpu state based on input parameters - * Only works when run as root - */ -int set_gpu_state(struct GameModeGPUInfo *info); diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index a892198..ea773cf 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -35,6 +35,24 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-control.h" +// TODO +// Apply Nvidia GPU settings (CoolBits will be needed) +// Apply AMD GPU settings (Will need user changing pwm1_enable) +// Provide documentation on optimisations +// Intel? +// Gather GPU type and information automatically if possible + +// NVIDIA +// Running these commands: +// nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' +// nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' + +// AMD +// We'll want to set both the following: +// core: device/pp_sclk_od (0%+ additional) +// mem: device/pp_mclk_od (0%+ additional) +// Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ + /* Helper to quit with usage */ static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; @@ -44,6 +62,36 @@ static void print_usage_and_exit(void) exit(EXIT_FAILURE); } +/** + * Get the gpu state + * Populates the struct with the GPU info on the system + */ +int get_gpu_state(struct GameModeGPUInfo *info) +{ + fprintf(stderr, "Fetching GPU state is currently unimplemented!\n"); + return -1; +} + +/** + * Set the gpu state based on input parameters + * Only works when run as root + */ +int set_gpu_state(struct GameModeGPUInfo *info) +{ + if (!info) + return -1; + + switch (info->vendor) { + case Vendor_NVIDIA: + break; + case Vendor_Intel: + break; + default: + break; + } + return 0; +} + /* Helper to get and verify vendor value */ static long get_vendor(const char *val) { diff --git a/daemon/meson.build b/daemon/meson.build index 1c57061..b088adc 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -2,7 +2,6 @@ common_sources = [ 'logging.c', 'governors-query.c', - 'gpu-control.c', ] daemon_common = static_library( From bd5baccc6790d1f414704944fbab08efbf51e1a7 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 18:47:18 +0000 Subject: [PATCH 25/42] Hook up individual set state functions --- daemon/gpuclockctl.c | 60 ++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index ea773cf..339298e 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -42,17 +42,6 @@ POSSIBILITY OF SUCH DAMAGE. // Intel? // Gather GPU type and information automatically if possible -// NVIDIA -// Running these commands: -// nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' -// nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' - -// AMD -// We'll want to set both the following: -// core: device/pp_sclk_od (0%+ additional) -// mem: device/pp_mclk_od (0%+ additional) -// Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ - /* Helper to quit with usage */ static const char *usage_text = "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; @@ -69,26 +58,39 @@ static void print_usage_and_exit(void) int get_gpu_state(struct GameModeGPUInfo *info) { fprintf(stderr, "Fetching GPU state is currently unimplemented!\n"); - return -1; + return info != NULL; } /** - * Set the gpu state based on input parameters - * Only works when run as root + * Set the gpu state based on input parameters on Nvidia */ -int set_gpu_state(struct GameModeGPUInfo *info) +int set_gpu_state_nv(struct GameModeGPUInfo *info) { - if (!info) + if(info->vendor != Vendor_NVIDIA ) return -1; - switch (info->vendor) { - case Vendor_NVIDIA: - break; - case Vendor_Intel: - break; - default: - break; - } + // Running these commands: + // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' + // nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' + + fprintf(stderr, "Setting GPU parameters on NVIDIA is currently unimplemented!\n"); + return 0; +} + +/** + * Set the gpu state based on input parameters on amd + */ +int set_gpu_state_amd(struct GameModeGPUInfo *info) +{ + if(info->vendor != Vendor_AMD) + return -1; + + // We'll want to set both the following: + // core: device/pp_sclk_od (0%+ additional) + // mem: device/pp_mclk_od (0%+ additional) + // Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ + + fprintf(stderr, "Setting GPU parameters on AMD is currently unimplemented!\n"); return 0; } @@ -171,7 +173,15 @@ int main(int argc, char *argv[]) if (info.vendor == Vendor_NVIDIA) printf("on Performance Level %ld\n", info.nv_perf_level); - return set_gpu_state(&info); + switch (info.vendor) { + case Vendor_NVIDIA: + return set_gpu_state_nv(&info); + case Vendor_AMD: + return set_gpu_state_amd(&info); + default: + printf("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", (short)info.vendor); + break; + } } else { print_usage_and_exit(); } From f5e7fa3222f5cf65c7dffed939dd46756372de36 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 19:20:16 +0000 Subject: [PATCH 26/42] Set up overclocking calls on NVidia These require the coolbits plugin to be activated on nvidia-xsettings --- daemon/external-helper.c | 1 - daemon/gpuclockctl.c | 36 +++++++++++++++++++++++++++++++----- daemon/meson.build | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/daemon/external-helper.c b/daemon/external-helper.c index 096317e..bd5d323 100644 --- a/daemon/external-helper.c +++ b/daemon/external-helper.c @@ -31,7 +31,6 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE -#include "config.h" #include "logging.h" #include diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 339298e..f66ebcc 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "logging.h" +#include "external-helper.h" #include "gpu-control.h" // TODO @@ -66,14 +67,39 @@ int get_gpu_state(struct GameModeGPUInfo *info) */ int set_gpu_state_nv(struct GameModeGPUInfo *info) { - if(info->vendor != Vendor_NVIDIA ) + if (info->vendor != Vendor_NVIDIA) return -1; - // Running these commands: - // nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=1400' - // nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=50' + // These commands don't technically even need root + + /* Set the GPUGraphicsClockOffset parameter */ + char core_arg[64]; + snprintf(core_arg, + 64, + "[gpu:%ld]/GPUGraphicsClockOffset[%ld]=%ld", + info->device, + info->nv_perf_level, + info->core); + const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", core_arg, NULL }; + if (run_external_process(exec_args_core) != 0) { + LOG_ERROR("ERROR: Failed to set %s!\n", core_arg); + return -1; + } + + /* Set the GPUMemoryTransferRateOffset parameter */ + char mem_arg[64]; + snprintf(mem_arg, + 64, + "[gpu:%ld]/GPUMemoryTransferRateOffset[%ld]=%ld", + info->device, + info->nv_perf_level, + info->mem); + const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", mem_arg, NULL }; + if (run_external_process(exec_args_mem) != 0) { + LOG_ERROR("ERROR: Failed to set %s!\n", mem_arg); + return -1; + } - fprintf(stderr, "Setting GPU parameters on NVIDIA is currently unimplemented!\n"); return 0; } diff --git a/daemon/meson.build b/daemon/meson.build index b088adc..e6c3f7d 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -2,6 +2,7 @@ common_sources = [ 'logging.c', 'governors-query.c', + 'external-helper.c', ] daemon_common = static_library( @@ -27,7 +28,6 @@ daemon_sources = [ 'gamemode-gpu.c', 'daemonize.c', 'dbus_messaging.c', - 'external-helper.c', 'daemon_config.c', ] From d00a9997f1674f6ff31b8e3dfa064dcb5242a358 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 3 Feb 2019 19:20:50 +0000 Subject: [PATCH 27/42] Correct log message --- daemon/external-helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/external-helper.c b/daemon/external-helper.c index bd5d323..da3db90 100644 --- a/daemon/external-helper.c +++ b/daemon/external-helper.c @@ -60,7 +60,7 @@ int run_external_process(const char *const *exec_args) * http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html */ if ((r = execv(exec_args[0], (char *const *)exec_args)) != 0) { - LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[1], strerror(errno)); + LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[0], strerror(errno)); exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); From 2aa2ca2f7d7f7a300dfa44e63257f2f0b584f180 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Mon, 4 Feb 2019 17:05:38 +0000 Subject: [PATCH 28/42] Implement AMD overclocking using AMDGPU Again, simply set values based on our inputs, with appropriate error messages --- daemon/gpuclockctl.c | 45 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index f66ebcc..099cc49 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -103,20 +103,51 @@ int set_gpu_state_nv(struct GameModeGPUInfo *info) return 0; } +/* + * Sets the value in a file in the AMDGPU driver config + * Files are: + * /sys/class/drm/card0/device/pp_sclk_od + * /sys/class/drm/card0/device/pp_mclk_od + */ +static int set_gpu_state_amd_file(const char *filename, long device, long value) +{ + const char *drm_path = "/sys/class/drm/card%ld/device/%s"; + char path[64]; + snprintf(path, 64, drm_path, device, filename); + + FILE *file = fopen(path, "w"); + if (!file) { + LOG_ERROR("ERROR: Could not open %s for write (%s)!\n", path, strerror(errno)); + return -1; + } + + if (fprintf(file, "%ld", value) < 0) { + LOG_ERROR("ERROR: Could not write to %s (%s)!\n", path, strerror(errno)); + return -1; + } + + if (fclose(file) != 0) { + LOG_ERROR("ERROR: Could not close %s after writing (%s)!\n", path, strerror(errno)); + return -1; + } + + return 0; +} + /** - * Set the gpu state based on input parameters on amd + * Set the gpu state based on input parameters on amd */ int set_gpu_state_amd(struct GameModeGPUInfo *info) { - if(info->vendor != Vendor_AMD) + if (info->vendor != Vendor_AMD) return -1; - // We'll want to set both the following: - // core: device/pp_sclk_od (0%+ additional) - // mem: device/pp_mclk_od (0%+ additional) - // Guide from https://www.maketecheasier.com/overclock-amd-gpu-linux/ + // Set the the core and mem clock speeds using the OverDrive files + if (set_gpu_state_amd_file("pp_sclk_od", info->device, info->core) != 0) + return -1; + if (set_gpu_state_amd_file("pp_mclk_od", info->device, info->mem) != 0) + return -1; - fprintf(stderr, "Setting GPU parameters on AMD is currently unimplemented!\n"); return 0; } From a488af5741cd77ca82ce9f0606fd793cefb44064 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Mon, 4 Feb 2019 17:06:04 +0000 Subject: [PATCH 29/42] Lower the amd overclock hard limit 20 appears to be the limit in the kernel Adjust nvidia message as well --- daemon/gamemode-gpu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 7bc1b80..e861515 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -126,7 +126,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) config_get_nv_perf_level(config, &new_info->nv_perf_level); if (new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) { LOG_ERROR( - "ERROR: NVIDIA Performance level value invalid (%ld), will not apply " + "ERROR: NVIDIA Performance level value likely invalid (%ld), will not apply " "optimisations!\n", new_info->nv_perf_level); free(new_info); @@ -138,13 +138,15 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) config_get_amd_core_clock_percentage(config, &new_info->core); config_get_amd_mem_clock_percentage(config, &new_info->mem); - /* Reject values over 25% + /* Reject values over 20% * If a user wants to go into very unsafe levels they can recompile + * As far as I can tell the driver doesn't allow values over 20 anyway */ - const int amd_hard_limit = 25; + const int amd_hard_limit = 20; if (new_info->core > amd_hard_limit || new_info->mem > amd_hard_limit) { - LOG_ERROR("ERROR AMD Overclock value above safety level of %d%%, will not overclock!\n", - amd_hard_limit); + LOG_ERROR( + "ERROR: AMD Overclock value above safety level of %d%%, will not overclock!\n", + amd_hard_limit); LOG_ERROR("amd_core_clock_percentage:%ld amd_mem_clock_percentage:%ld\n", new_info->core, new_info->mem); From db0f8f91f344c5b040cc8ebb333f977ed3a93eca Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Mon, 4 Feb 2019 17:20:48 +0000 Subject: [PATCH 30/42] Update TODO comments --- daemon/gamemode-gpu.c | 8 +++----- daemon/gpuclockctl.c | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index e861515..7304272 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -73,8 +73,6 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) config_get_gpu_vendor(config, &new_info->vendor); config_get_gpu_device(config, &new_info->device); - /* TODO: Detect the GPU vendor and device automatically when these aren't set */ - /* verify device ID */ if (new_info->device == -1) { LOG_ERROR( @@ -202,15 +200,15 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) char nv_perf_level[4]; snprintf(nv_perf_level, 4, "%ld", info->nv_perf_level); - // TODO: Actually pass right arguments + // Set up our command line to pass to gpuclockctl const char *const exec_args[] = { "/usr/bin/pkexec", LIBEXECDIR "/gpuclockctl", vendor, device, "set", - apply ? core : "0", /* For now simply reset to zero */ - apply ? mem : "0", /* could in the future store default values for reset */ + apply ? core : "0", + apply ? mem : "0", info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ NULL, }; diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 099cc49..d42e718 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -37,11 +37,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "gpu-control.h" // TODO -// Apply Nvidia GPU settings (CoolBits will be needed) -// Apply AMD GPU settings (Will need user changing pwm1_enable) -// Provide documentation on optimisations -// Intel? -// Gather GPU type and information automatically if possible +// Intel support - https://blog.ffwll.ch/2013/03/overclocking-your-intel-gpu-on-linux.html +// AMD - Allow setting fan speed as well +// Store baseline values with get_gpu_state to apply when leaving gamemode /* Helper to quit with usage */ static const char *usage_text = From 0170a72634cfbcac5ac1cb71cd50dcee529e235e Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 5 Feb 2019 21:31:42 +0000 Subject: [PATCH 31/42] Fix formatting of renice value comment --- daemon/daemon_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 69d0b0a..561be03 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -281,7 +281,7 @@ static void load_config_files(GameModeConfig *self) memset(self->softrealtime, 0, sizeof(self->softrealtime)); memset(self->apply_gpu_optimisations, 0, sizeof(self->apply_gpu_optimisations)); self->inhibit_screensaver = 1; /* Defaults to on */ - self->renice = 4; /* default value of 4 */ + self->renice = 4; /* default value of 4 */ self->reaper_frequency = DEFAULT_REAPER_FREQ; self->gpu_vendor = 0; self->gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ From d18f3fc584a2472168a857cc64fdfb10a75353a2 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 6 Feb 2019 17:12:54 +0000 Subject: [PATCH 32/42] Remove duplicate ERROR strings from error logs Now visible in 5898538 --- daemon/gamemode-gpu.c | 2 +- daemon/gpuclockctl.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 7304272..6a162b4 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -214,7 +214,7 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info, bool apply) }; if (run_external_process(exec_args) != 0) { - LOG_ERROR("ERROR: Failed to call gpuclockctl, could not apply optimisations!\n"); + LOG_ERROR("Failed to call gpuclockctl, could not apply optimisations!\n"); return -1; } diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index d42e718..02280de 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -80,7 +80,7 @@ int set_gpu_state_nv(struct GameModeGPUInfo *info) info->core); const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", core_arg, NULL }; if (run_external_process(exec_args_core) != 0) { - LOG_ERROR("ERROR: Failed to set %s!\n", core_arg); + LOG_ERROR("Failed to set %s!\n", core_arg); return -1; } @@ -94,7 +94,7 @@ int set_gpu_state_nv(struct GameModeGPUInfo *info) info->mem); const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", mem_arg, NULL }; if (run_external_process(exec_args_mem) != 0) { - LOG_ERROR("ERROR: Failed to set %s!\n", mem_arg); + LOG_ERROR("Failed to set %s!\n", mem_arg); return -1; } @@ -115,17 +115,17 @@ static int set_gpu_state_amd_file(const char *filename, long device, long value) FILE *file = fopen(path, "w"); if (!file) { - LOG_ERROR("ERROR: Could not open %s for write (%s)!\n", path, strerror(errno)); + LOG_ERROR("Could not open %s for write (%s)!\n", path, strerror(errno)); return -1; } if (fprintf(file, "%ld", value) < 0) { - LOG_ERROR("ERROR: Could not write to %s (%s)!\n", path, strerror(errno)); + LOG_ERROR("Could not write to %s (%s)!\n", path, strerror(errno)); return -1; } if (fclose(file) != 0) { - LOG_ERROR("ERROR: Could not close %s after writing (%s)!\n", path, strerror(errno)); + LOG_ERROR("Could not close %s after writing (%s)!\n", path, strerror(errno)); return -1; } @@ -155,7 +155,7 @@ static long get_vendor(const char *val) char *end; long ret = strtol(val, &end, 0); if (!GPUVendorValid(ret) || end == val) { - LOG_ERROR("ERROR: Invalid GPU Vendor passed (0x%04x)!\n", (unsigned short)ret); + LOG_ERROR("Invalid GPU Vendor passed (0x%04x)!\n", (unsigned short)ret); print_usage_and_exit(); } return ret; @@ -167,7 +167,7 @@ static long get_device(const char *val) char *end; long ret = strtol(val, &end, 10); if (ret < 0 || end == val) { - LOG_ERROR("ERROR: Invalid GPU device passed (%ld)!\n", ret); + LOG_ERROR("Invalid GPU device passed (%ld)!\n", ret); print_usage_and_exit(); } return ret; @@ -179,7 +179,7 @@ static long get_generic_value(const char *val) char *end; long ret = strtol(val, &end, 10); if (ret < 0 || end == val) { - LOG_ERROR("ERROR: Invalid value passed (%ld)!\n", ret); + LOG_ERROR("Invalid value passed (%ld)!\n", ret); print_usage_and_exit(); } return ret; From 16ade5c1c9f18bf67336c0ea1faa5df69222542d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 6 Feb 2019 17:20:08 +0000 Subject: [PATCH 33/42] Adjust the TODO list --- daemon/gpuclockctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 02280de..a5fb773 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -36,10 +36,11 @@ POSSIBILITY OF SUCH DAMAGE. #include "external-helper.h" #include "gpu-control.h" -// TODO -// Intel support - https://blog.ffwll.ch/2013/03/overclocking-your-intel-gpu-on-linux.html -// AMD - Allow setting fan speed as well -// Store baseline values with get_gpu_state to apply when leaving gamemode +/* Plausible extras to add: + * Intel support - https://blog.ffwll.ch/2013/03/overclocking-your-intel-gpu-on-linux.html + * AMD - Allow setting fan speed as well + * Store baseline values with get_gpu_state to apply when leaving gamemode + */ /* Helper to quit with usage */ static const char *usage_text = From 4cee59cde38e28001481a29558940406b4f01fca Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 6 Feb 2019 17:26:33 +0000 Subject: [PATCH 34/42] Add a note about overclocking to the README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4f8c54f..fbd7a43 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Currently GameMode includes support for optimisations including: * I/O Priority * Kernel Scheduler (`SCHED_ISO`) * Screensaver inhibiting +* GPU Overclocking (Nvidia and AMD) * Custom scripts Issues with GameMode should be reported here in the issues section, and not reported to Feral directly. @@ -106,6 +107,9 @@ If you are unsure, `bootstrap.sh` will warn you if your system lacks CPU governo Scripts and other features will still work. +### GPU Optimisations +GameMode is able to automatically apply GPU overclocks when activated. AMD overclocking currently requires the amdgpu kernel module, and Nvidia requires the `coolbits` extension to be enabled in the Nvidia settings. It is very much encouraged for users to find out their own overclocking limits manually before venturing into configuring them in gamemode, and activating this feature in gamemode assumes you take responsibility for the effects of said overclocks. More information can be found in the `example/gamemoded.ini` file. + --- ## Developers From ba20df5c47859e077d9555000e09b6f7bce4b387 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 6 Feb 2019 18:00:36 +0000 Subject: [PATCH 35/42] Fix headers for travis --- daemon/gamemode-gpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 6a162b4..c50f1c0 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -34,10 +34,11 @@ POSSIBILITY OF SUCH DAMAGE. #include "config.h" #include "external-helper.h" -#include "gamemode.h" #include "helpers.h" #include "logging.h" +#include "gamemode.h" + #include "daemon_config.h" #include "gpu-control.h" From d1b7c49bc258d20feda8225c2316d515cd01f832 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 6 Feb 2019 19:03:15 +0000 Subject: [PATCH 36/42] Remove more duplicate ERROR: logs --- daemon/gamemode-gpu.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index c50f1c0..0ce76df 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -77,7 +77,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* verify device ID */ if (new_info->device == -1) { LOG_ERROR( - "ERROR: Invalid gpu_device value set in configuration, will not apply " + "Invalid gpu_device value set in configuration, will not apply " "optimisations!\n"); free(new_info); return -1; @@ -86,7 +86,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* verify GPU vendor */ if (!GPUVendorValid(new_info->vendor)) { LOG_ERROR( - "ERROR: Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " + "Invalid gpu_vendor value (0x%04x) set in configuration, will not apply " "optimisations!\n", (unsigned int)new_info->vendor); LOG_ERROR("Possible values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", @@ -110,7 +110,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) const int nv_mem_hard_limit = 2000; if (new_info->core > nv_core_hard_limit || new_info->mem > nv_mem_hard_limit) { LOG_ERROR( - "ERROR: NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will " + "NVIDIA Overclock value above safety levels of +%d (core) +%d (mem), will " "not overclock!\n", nv_core_hard_limit, nv_mem_hard_limit); @@ -125,7 +125,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) config_get_nv_perf_level(config, &new_info->nv_perf_level); if (new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) { LOG_ERROR( - "ERROR: NVIDIA Performance level value likely invalid (%ld), will not apply " + "NVIDIA Performance level value likely invalid (%ld), will not apply " "optimisations!\n", new_info->nv_perf_level); free(new_info); @@ -143,9 +143,8 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) */ const int amd_hard_limit = 20; if (new_info->core > amd_hard_limit || new_info->mem > amd_hard_limit) { - LOG_ERROR( - "ERROR: AMD Overclock value above safety level of %d%%, will not overclock!\n", - amd_hard_limit); + LOG_ERROR("AMD Overclock value above safety level of %d%%, will not overclock!\n", + amd_hard_limit); LOG_ERROR("amd_core_clock_percentage:%ld amd_mem_clock_percentage:%ld\n", new_info->core, new_info->mem); From 00ed82f8e499d07bcfd98c42acb4a52e43e5ba1e Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 9 Feb 2019 19:08:06 +0000 Subject: [PATCH 37/42] Extend the nvidia command size to 128 It was exactly 64 for small arguments, but that fails for larger overlocks --- daemon/gpuclockctl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index a5fb773..51971f0 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -72,9 +72,9 @@ int set_gpu_state_nv(struct GameModeGPUInfo *info) // These commands don't technically even need root /* Set the GPUGraphicsClockOffset parameter */ - char core_arg[64]; + char core_arg[128]; snprintf(core_arg, - 64, + 128, "[gpu:%ld]/GPUGraphicsClockOffset[%ld]=%ld", info->device, info->nv_perf_level, @@ -86,9 +86,9 @@ int set_gpu_state_nv(struct GameModeGPUInfo *info) } /* Set the GPUMemoryTransferRateOffset parameter */ - char mem_arg[64]; + char mem_arg[128]; snprintf(mem_arg, - 64, + 128, "[gpu:%ld]/GPUMemoryTransferRateOffset[%ld]=%ld", info->device, info->nv_perf_level, From f86f57af0e2dbc730ae0eba9a5d392c5a2fac2b4 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Feb 2019 10:42:30 +0000 Subject: [PATCH 38/42] Add a little more flavor text to remind users that both AMD and Nvidia do have automatic overclocks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbd7a43..83e8250 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ If you are unsure, `bootstrap.sh` will warn you if your system lacks CPU governo Scripts and other features will still work. ### GPU Optimisations -GameMode is able to automatically apply GPU overclocks when activated. AMD overclocking currently requires the amdgpu kernel module, and Nvidia requires the `coolbits` extension to be enabled in the Nvidia settings. It is very much encouraged for users to find out their own overclocking limits manually before venturing into configuring them in gamemode, and activating this feature in gamemode assumes you take responsibility for the effects of said overclocks. More information can be found in the `example/gamemoded.ini` file. +GameMode is able to automatically apply GPU overclocks when activated. AMD overclocking currently requires the amdgpu kernel module, and Nvidia requires the `coolbits` extension to be enabled in the Nvidia settings. It is very much encouraged for users to find out their own overclocking limits manually before venturing into configuring them in gamemode, and activating this feature in gamemode assumes you take responsibility for the effects of said overclocks. More information can be found in the `example/gamemoded.ini` file. Note that both Nvidia (GPUBoost) and AMD (Overdrive) devices and drivers already attempt to internally overclock if possible, but it is still common for enthusiasts to want to manually push the upper threshold. --- ## Developers From 8584558f9cc94f7772ab412b3b3b95e74a842e9c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Feb 2019 11:01:48 +0000 Subject: [PATCH 39/42] Make sure gpuclockctl inherits the DISPLAY and XAUTHORITY variables so nvidia-settings works --- data/com.feralinteractive.GameMode.policy.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/com.feralinteractive.GameMode.policy.in b/data/com.feralinteractive.GameMode.policy.in index b0dce0d..7de96eb 100644 --- a/data/com.feralinteractive.GameMode.policy.in +++ b/data/com.feralinteractive.GameMode.policy.in @@ -32,6 +32,7 @@ yes @LIBEXECDIR@/gpuclockctl + true From 547b7943fda09d9fd3e1ea425eb06a7a1c03342b Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 13 Feb 2019 17:22:45 +0000 Subject: [PATCH 40/42] Remove r in "statesr" --- data/com.feralinteractive.GameMode.policy.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/com.feralinteractive.GameMode.policy.in b/data/com.feralinteractive.GameMode.policy.in index 7de96eb..78cc5ee 100644 --- a/data/com.feralinteractive.GameMode.policy.in +++ b/data/com.feralinteractive.GameMode.policy.in @@ -24,7 +24,7 @@ - Modify the GPU clock statesr + Modify the GPU clock states Authentication is required to modify the GPU clock states no From 10d1c12aaba83e80def3b59ccff3bb6f57144526 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 13 Feb 2019 17:25:24 +0000 Subject: [PATCH 41/42] Correct capitalisation and ini file path in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83e8250..c625e67 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ If you are unsure, `bootstrap.sh` will warn you if your system lacks CPU governo Scripts and other features will still work. ### GPU Optimisations -GameMode is able to automatically apply GPU overclocks when activated. AMD overclocking currently requires the amdgpu kernel module, and Nvidia requires the `coolbits` extension to be enabled in the Nvidia settings. It is very much encouraged for users to find out their own overclocking limits manually before venturing into configuring them in gamemode, and activating this feature in gamemode assumes you take responsibility for the effects of said overclocks. More information can be found in the `example/gamemoded.ini` file. Note that both Nvidia (GPUBoost) and AMD (Overdrive) devices and drivers already attempt to internally overclock if possible, but it is still common for enthusiasts to want to manually push the upper threshold. +GameMode is able to automatically apply GPU overclocks when activated. AMD overclocking currently requires the amdgpu kernel module, and Nvidia requires the `coolbits` extension to be enabled in the Nvidia settings. It is very much encouraged for users to find out their own overclocking limits manually before venturing into configuring them in GameMode, and activating this feature in GameMode assumes you take responsibility for the effects of said overclocks. More information can be found in the `example/gamemode.ini` file. Note that both Nvidia (GPUBoost) and AMD (Overdrive) devices and drivers already attempt to internally overclock if possible, but it is still common for enthusiasts to want to manually push the upper threshold. --- ## Developers From c7da9ff9ea6293bfb950a9092d4b5546bce58101 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Fri, 15 Feb 2019 18:20:52 +0000 Subject: [PATCH 42/42] Reword advice in the example ini file to clear up confusion with the PCI ID --- example/gamemode.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/gamemode.ini b/example/gamemode.ini index d9fb48e..da101e4 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -43,7 +43,7 @@ inhibit_screensaver=1 ; Setting this to the keyphrase "accept-responsibility" will allow gamemode to apply GPU optimisations such as overclocks ;apply_gpu_optimisations=0 -; Set these to specify which vendor and device ID you want apply optimisations to +; You must set these to tell gamemode the Vendor of your graphics card, as well it's device number on the system (usually 0) ; Vendor must be one of 0x10de (NVIDIA), 0x1002 (AMD) or 0x8086 (Intel) ;gpu_vendor=0x0000 ;gpu_device=0