From eb9bb0cdf72859de26b417a357b8e2cba3008884 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Mar 2019 11:54:54 +0000 Subject: [PATCH 1/5] Add notes on values for nv_powermizer_mode Instead of just suggesting values --- example/gamemode.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/gamemode.ini b/example/gamemode.ini index a0ab428..02102fb 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -49,7 +49,8 @@ inhibit_screensaver=1 ; Nvidia specific settings ; Requires the coolbits extension activated in nvidia-xconfig ; This corresponds to the desired GPUPowerMizerMode -; Generally "Adaptive"=0 "Prefer Maximum Performance"=1 and "Auto"=2) +; "Adaptive"=0 "Prefer Maximum Performance"=1 and "Auto"=2 +; See NV_CTRL_GPU_POWER_MIZER_MODE and friends in https://github.com/NVIDIA/nvidia-settings/blob/master/src/libXNVCtrl/NVCtrl.h ;nv_powermizer_mode=1 ; This corresponds to the performance level to edit in nvidia-xconfig (usually the highest level - on older cards 1, newer cards can be 4 or higher) From 5e5cae31d36d4d02b9e15ce265c489f26e9add30 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Mar 2019 12:38:31 +0000 Subject: [PATCH 2/5] Explain a little more about nv_perf_level We think this was a little confusing, so explain some more. --- example/gamemode.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/gamemode.ini b/example/gamemode.ini index 02102fb..99ee74b 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -53,7 +53,9 @@ inhibit_screensaver=1 ; See NV_CTRL_GPU_POWER_MIZER_MODE and friends in https://github.com/NVIDIA/nvidia-settings/blob/master/src/libXNVCtrl/NVCtrl.h ;nv_powermizer_mode=1 -; This corresponds to the performance level to edit in nvidia-xconfig (usually the highest level - on older cards 1, newer cards can be 4 or higher) +; This corresponds to the performance level to edit in nvidia-xconfig +; Set this to the highest level shown in the nvidia settings power mizer settings +; Or from the command line check the highest perf value listed by "nvidia-settings -q [gpu:0]/GPUPerfModes" ;nv_perf_level=1 ; (these two are Mhz offsets from the baseline, ie. 0 applies no change) ;nv_core_clock_mhz_offset=0 From 873d0a224b56f223194d45e0b86804ecac5a9417 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Mar 2019 14:11:22 +0000 Subject: [PATCH 3/5] Remove passing the vendor to gpuclockctl --- daemon/gamemode-gpu.c | 38 ++--------------------- daemon/gpu-control.c | 72 +++++++++++++++++++++++++++++++++++++++++++ daemon/gpu-control.h | 3 ++ daemon/gpuclockctl.c | 67 ++++++++++++++++++++-------------------- daemon/meson.build | 1 + 5 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 daemon/gpu-control.c diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index f63767c..db3509b 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -47,8 +47,6 @@ POSSIBILITY OF SUCH DAMAGE. */ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) { - int status = 0; - /* Verify input, this is programmer error */ if (!info || *info) FATAL_ERROR("Invalid GameModeGPUInfo passed to %s", __func__); @@ -83,33 +81,9 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) } /* Fill in GPU vendor */ - char path[64] = { 0 }; - if (snprintf(path, 64, "/sys/class/drm/card%ld/device/vendor", new_info->device) < 0) { - LOG_ERROR("snprintf failed, will not apply gpu optimisations!\n"); - return -1; - } - FILE *vendor = fopen(path, "r"); - if (!vendor) { - LOG_ERROR("Couldn't open vendor file at %s, will not apply gpu optimisations!\n", path); - return -1; - } - char buff[64]; - if (fgets(buff, 64, vendor) != NULL) { - new_info->vendor = strtol(buff, NULL, 0); - } else { - LOG_ERROR("Coudn't read contents of file %s, will not apply optimisations!\n", path); - return -1; - } - - /* verify GPU vendor */ + new_info->vendor = gamemode_get_gpu_vendor(new_info->device); if (!GPUVendorValid(new_info->vendor)) { - LOG_ERROR("Unknown vendor value (0x%04x) found, cannot apply optimisations!\n", - (unsigned int)new_info->vendor); - LOG_ERROR("Known values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", - Vendor_NVIDIA, - Vendor_AMD, - Vendor_Intel); - free(new_info); + LOG_ERROR("Found invalid vendor, will not apply optimisations!\n"); return -1; } @@ -168,7 +142,7 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) /* Give back the new gpu info */ *info = new_info; - return status; + return 0; } /* Simply used to free the GPU info object */ @@ -196,8 +170,6 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) LOG_MSG("Requesting GPU optimisations on device:%ld\n", info->device); /* Generate the input strings */ - char vendor[7]; - snprintf(vendor, 7, "0x%04x", (short)info->vendor); char device[4]; snprintf(device, 4, "%ld", info->device); @@ -214,7 +186,6 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) const char *const exec_args[] = { "/usr/bin/pkexec", LIBEXECDIR "/gpuclockctl", - vendor, device, "set", info->vendor == Vendor_NVIDIA ? nv_core : info->amd_performance_level, @@ -238,8 +209,6 @@ int game_mode_get_gpu(GameModeGPUInfo *info) return 0; /* 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 nv_perf_level[4]; @@ -249,7 +218,6 @@ int game_mode_get_gpu(GameModeGPUInfo *info) // This doesn't need pkexec as get does not need elevated perms const char *const exec_args[] = { LIBEXECDIR "/gpuclockctl", - vendor, device, "get", info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ diff --git a/daemon/gpu-control.c b/daemon/gpu-control.c new file mode 100644 index 0000000..e665d49 --- /dev/null +++ b/daemon/gpu-control.c @@ -0,0 +1,72 @@ +/* + +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. + + */ +#include "gpu-control.h" +#include "logging.h" + +#include + +/* Get the vendor for a device */ +enum GPUVendor gamemode_get_gpu_vendor(long device) +{ + enum GPUVendor vendor = Vendor_Invalid; + + /* Fill in GPU vendor */ + char path[64] = { 0 }; + if (snprintf(path, 64, "/sys/class/drm/card%ld/device/vendor", device) < 0) { + LOG_ERROR("snprintf failed, will not apply gpu optimisations!\n"); + return Vendor_Invalid; + } + FILE *file = fopen(path, "r"); + if (!file) { + LOG_ERROR("Couldn't open vendor file at %s, will not apply gpu optimisations!\n", path); + return Vendor_Invalid; + } + char buff[64]; + if (fgets(buff, 64, file) != NULL) { + vendor = strtol(buff, NULL, 0); + } else { + LOG_ERROR("Coudn't read contents of file %s, will not apply optimisations!\n", path); + return Vendor_Invalid; + } + + /* verify GPU vendor */ + if (!GPUVendorValid(vendor)) { + LOG_ERROR("Unknown vendor value (0x%04x) found, cannot apply optimisations!\n", + (unsigned int)vendor); + LOG_ERROR("Known values are: 0x%04x (NVIDIA) 0x%04x (AMD) 0x%04x (Intel)\n", + Vendor_NVIDIA, + Vendor_AMD, + Vendor_Intel); + return Vendor_Invalid; + } + + return vendor; +} diff --git a/daemon/gpu-control.h b/daemon/gpu-control.h index 325605f..e58b11b 100644 --- a/daemon/gpu-control.h +++ b/daemon/gpu-control.h @@ -55,3 +55,6 @@ struct GameModeGPUInfo { char amd_performance_level[CONFIG_VALUE_MAX]; /* The AMD performance level set to */ }; + +/* Get the vendor for a device */ +enum GPUVendor gamemode_get_gpu_vendor(long device); diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index 21f6ad2..f29093b 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -284,18 +284,6 @@ static int set_gpu_state_amd(struct GameModeGPUInfo *info) return 0; } -/* 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("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) { @@ -325,20 +313,23 @@ static long get_generic_value(const char *val) */ int main(int argc, char *argv[]) { - if (argc >= 4 && strncmp(argv[3], "get", 3) == 0) { + if (argc >= 3 && strncmp(argv[2], "get", 3) == 0) { /* Get and verify the vendor and device */ struct GameModeGPUInfo info; memset(&info, 0, sizeof(info)); - info.vendor = get_vendor(argv[1]); - info.device = get_device(argv[2]); + info.device = get_device(argv[1]); + info.vendor = gamemode_get_gpu_vendor(info.device); /* Fetch the state and print it out */ switch (info.vendor) { case Vendor_NVIDIA: - info.nv_perf_level = -1; - if (argc > 4) - info.nv_perf_level = get_generic_value(argv[4]); - /* Get nvidia power level */ + if (argc < 3) { + LOG_ERROR("Must pass perf_level for nvidia gpu!\n"); + print_usage_and_exit(); + } + info.nv_perf_level = get_generic_value(argv[3]); + + /* Get nvidia info */ if (get_gpu_state_nv(&info) != 0) exit(EXIT_FAILURE); printf("%ld %ld %ld\n", info.nv_core, info.nv_mem, info.nv_powermizer_mode); @@ -349,33 +340,44 @@ int main(int argc, char *argv[]) printf("%s\n", info.amd_performance_level); break; default: - printf("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", (short)info.vendor); + LOG_ERROR("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", + (short)info.vendor); break; } - } else if (argc >= 5 && argc <= 8 && strncmp(argv[3], "set", 3) == 0) { + } else if (argc >= 4 && argc <= 7 && strncmp(argv[2], "set", 3) == 0) { /* Get and verify the vendor and device */ struct GameModeGPUInfo info; memset(&info, 0, sizeof(info)); - info.vendor = get_vendor(argv[1]); - info.device = get_device(argv[2]); + info.device = get_device(argv[1]); + info.vendor = gamemode_get_gpu_vendor(info.device); switch (info.vendor) { case Vendor_NVIDIA: - info.nv_core = get_generic_value(argv[4]); - info.nv_mem = get_generic_value(argv[5]); - info.nv_perf_level = -1; - if (argc > 6) - info.nv_perf_level = get_generic_value(argv[6]); + if (argc < 5) { + LOG_ERROR("Must pass at least 5 arguments for nvidia gpu!\n"); + print_usage_and_exit(); + } + info.nv_core = get_generic_value(argv[3]); + info.nv_mem = get_generic_value(argv[4]); + info.nv_perf_level = get_generic_value(argv[5]); + + /* Optional */ info.nv_powermizer_mode = -1; - if (argc > 7) - info.nv_powermizer_mode = get_generic_value(argv[7]); + if (argc >= 5) + info.nv_powermizer_mode = get_generic_value(argv[6]); break; case Vendor_AMD: - strncpy(info.amd_performance_level, argv[4], CONFIG_VALUE_MAX); + if (argc < 3) { + LOG_ERROR("Must pass performance level for AMD gpu!\n"); + print_usage_and_exit(); + } + strncpy(info.amd_performance_level, argv[3], CONFIG_VALUE_MAX); break; default: - printf("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", (short)info.vendor); + LOG_ERROR("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", + (short)info.vendor); + print_usage_and_exit(); break; } @@ -389,7 +391,6 @@ int main(int argc, char *argv[]) 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 { diff --git a/daemon/meson.build b/daemon/meson.build index e6c3f7d..06cb183 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -3,6 +3,7 @@ common_sources = [ 'logging.c', 'governors-query.c', 'external-helper.c', + 'gpu-control.c', ] daemon_common = static_library( From fec32ac53d724cfebe67abaf7427ea17d7f1972c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sun, 10 Mar 2019 15:11:24 +0000 Subject: [PATCH 4/5] Remove the nv_perf_level config option and figure it out programmatically This also fixes the instances in testing where we don't have the nv overclock in use, but we do have the mode set Solves issues explaining the what the perf_level actually meant, and future proofs for any PR that wants to set individual perf levels --- daemon/daemon_config.c | 7 +- daemon/daemon_config.h | 1 - daemon/gamemode-gpu.c | 19 ----- daemon/gamemode-tests.c | 6 +- daemon/gpu-control.h | 3 +- daemon/gpuclockctl.c | 166 +++++++++++++++++++++++----------------- example/gamemode.ini | 7 +- 7 files changed, 105 insertions(+), 104 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index df24016..69eed10 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -91,7 +91,6 @@ struct GameModeConfig { long gpu_device; long nv_core_clock_mhz_offset; long nv_mem_clock_mhz_offset; - long nv_perf_level; long nv_powermizer_mode; char amd_performance_level[CONFIG_VALUE_MAX]; @@ -257,8 +256,6 @@ static int inih_handler(void *user, const char *section, const char *name, const valid = get_long_value(name, value, &self->values.nv_core_clock_mhz_offset); } else if (strcmp(name, "nv_mem_clock_mhz_offset") == 0) { valid = get_long_value(name, value, &self->values.nv_mem_clock_mhz_offset); - } else if (strcmp(name, "nv_perf_level") == 0) { - valid = get_long_value(name, value, &self->values.nv_perf_level); } else if (strcmp(name, "nv_powermizer_mode") == 0) { valid = get_long_value(name, value, &self->values.nv_powermizer_mode); } else if (strcmp(name, "amd_performance_level") == 0) { @@ -332,8 +329,9 @@ static void load_config_files(GameModeConfig *self) self->values.renice = 4; /* default value of 4 */ self->values.reaper_frequency = DEFAULT_REAPER_FREQ; self->values.gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ - self->values.nv_perf_level = -1; self->values.nv_powermizer_mode = -1; + self->values.nv_core_clock_mhz_offset = -1; + self->values.nv_mem_clock_mhz_offset = -1; self->values.script_timeout = 10; /* Default to 10 seconds for scripts */ /* @@ -585,7 +583,6 @@ void config_get_apply_gpu_optimisations(GameModeConfig *self, char value[CONFIG_ DEFINE_CONFIG_GET(gpu_device) DEFINE_CONFIG_GET(nv_core_clock_mhz_offset) DEFINE_CONFIG_GET(nv_mem_clock_mhz_offset) -DEFINE_CONFIG_GET(nv_perf_level) DEFINE_CONFIG_GET(nv_powermizer_mode) void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]) diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index d6795f5..538ef10 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -142,7 +142,6 @@ void config_get_apply_gpu_optimisations(GameModeConfig *self, char value[CONFIG_ long config_get_gpu_device(GameModeConfig *self); long config_get_nv_core_clock_mhz_offset(GameModeConfig *self); long config_get_nv_mem_clock_mhz_offset(GameModeConfig *self); -long config_get_nv_perf_level(GameModeConfig *self); long config_get_nv_powermizer_mode(GameModeConfig *self); void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]); diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index db3509b..c7ae96c 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -92,7 +92,6 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) case Vendor_NVIDIA: new_info->nv_core = config_get_nv_core_clock_mhz_offset(config); new_info->nv_mem = config_get_nv_mem_clock_mhz_offset(config); - new_info->nv_perf_level = config_get_nv_perf_level(config); new_info->nv_powermizer_mode = config_get_nv_powermizer_mode(config); /* Reject values over some guessed values @@ -113,18 +112,6 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) return -1; } - /* Sanity check the performance level value as well */ - /* Allow an invalid perf level if we've got the powermizer mode set */ - if (!(new_info->nv_perf_level == -1 && new_info->nv_powermizer_mode != -1) && - (new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16)) { - LOG_ERROR( - "NVIDIA Performance level value likely invalid (%ld), will not apply " - "optimisations!\n", - new_info->nv_perf_level); - free(new_info); - return -1; - } - break; case Vendor_AMD: config_get_amd_performance_level(config, new_info->amd_performance_level); @@ -177,8 +164,6 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) snprintf(nv_core, 8, "%ld", info->nv_core); char nv_mem[8]; snprintf(nv_mem, 8, "%ld", info->nv_mem); - char nv_perf_level[4]; - snprintf(nv_perf_level, 4, "%ld", info->nv_perf_level); char nv_powermizer_mode[4]; snprintf(nv_powermizer_mode, 4, "%ld", info->nv_powermizer_mode); @@ -190,7 +175,6 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) "set", info->vendor == Vendor_NVIDIA ? nv_core : info->amd_performance_level, info->vendor == Vendor_NVIDIA ? nv_mem : NULL, /* Only use this if Nvidia */ - info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ info->vendor == Vendor_NVIDIA ? nv_powermizer_mode : NULL, /* Only use this if Nvidia */ NULL, }; @@ -211,8 +195,6 @@ int game_mode_get_gpu(GameModeGPUInfo *info) /* Generate the input strings */ char device[4]; snprintf(device, 4, "%ld", info->device); - char nv_perf_level[4]; - snprintf(nv_perf_level, 4, "%ld", info->nv_perf_level); // Set up our command line to pass to gpuclockctl // This doesn't need pkexec as get does not need elevated perms @@ -220,7 +202,6 @@ int game_mode_get_gpu(GameModeGPUInfo *info) LIBEXECDIR "/gpuclockctl", device, "get", - info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ NULL, }; diff --git a/daemon/gamemode-tests.c b/daemon/gamemode-tests.c index 57b1746..540311d 100644 --- a/daemon/gamemode-tests.c +++ b/daemon/gamemode-tests.c @@ -456,9 +456,13 @@ int run_gpu_optimisation_tests(struct GameModeConfig *config) char original_amd_performance_level[CONFIG_VALUE_MAX]; strncpy(original_amd_performance_level, gpuinfo->amd_performance_level, CONFIG_VALUE_MAX); - /* account for when powermizer is not set */ + /* account for when settings are not set */ if (expected_nv_powermizer_mode == -1) expected_nv_powermizer_mode = original_nv_powermizer_mode; + if (expected_core == -1) + expected_core = original_nv_core; + if (expected_mem == -1) + expected_mem = original_nv_mem; /* Start gamemode and check the new values */ gamemode_request_start(); diff --git a/daemon/gpu-control.h b/daemon/gpu-control.h index e58b11b..fd9ec21 100644 --- a/daemon/gpu-control.h +++ b/daemon/gpu-control.h @@ -46,8 +46,7 @@ enum GPUVendor { /* Storage for GPU info*/ struct GameModeGPUInfo { long vendor; - long device; /* path to device, ie. /sys/class/drm/card#/ */ - long nv_perf_level; /* The Nvidia Performance Level to adjust */ + long device; /* path to device, ie. /sys/class/drm/card#/ */ long nv_core; /* Nvidia core clock */ long nv_mem; /* Nvidia mem clock */ diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index f29093b..e50f5db 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -40,6 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. #define NV_CORE_OFFSET_ATTRIBUTE "GPUGraphicsClockOffset" #define NV_MEM_OFFSET_ATTRIBUTE "GPUMemoryTransferRateOffset" #define NV_POWERMIZER_MODE_ATTRIBUTE "GPUPowerMizerMode" +#define NV_PERFMODES_ATTRIBUTE "GPUPerfModes" #define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s" #define NV_PERF_LEVEL_FORMAT "[%ld]" @@ -54,14 +55,16 @@ POSSIBILITY OF SUCH DAMAGE. /* Helper to quit with usage */ static const char *usage_text = - "usage: gpuclockctl PCI_ID DEVICE [get] [set CORE MEM [PERF_LEVEL]]]"; + "usage: gpuclockctl DEVICE {arg}\n\t\tget - return current values\n\t\tset [NV_CORE NV_MEM " + "NV_POWERMIZER_MODE | AMD_PERFORMANCE_LEVEL] - set current values"; static void print_usage_and_exit(void) { fprintf(stderr, "%s\n", usage_text); exit(EXIT_FAILURE); } -static int get_gpu_state_nv(struct GameModeGPUInfo *info) +/* Get the max nvidia perf level */ +static long get_max_perf_level_nv(struct GameModeGPUInfo *info) { if (info->vendor != Vendor_NVIDIA) return -1; @@ -69,48 +72,80 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) if (!getenv("DISPLAY")) LOG_ERROR("Getting Nvidia parameters requires DISPLAY to be set - will likely fail!\n"); + char arg[128] = { 0 }; + char buf[EXTERNAL_BUFFER_MAX] = { 0 }; + + snprintf(arg, 128, NV_ATTRIBUTE_FORMAT, info->device, NV_PERFMODES_ATTRIBUTE); + const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; + if (run_external_process(exec_args, buf, -1) != 0) { + LOG_ERROR("Failed to get %s!\n", arg); + return -1; + } + + char *ptr = strrchr(buf, ';'); + long level = -1; + if (!ptr || sscanf(ptr, "; perf=%ld", &level) != 1) { + LOG_ERROR( + "Output didn't match expected format, couldn't discern highest perf level from " + "nvidia-settings!\n"); + LOG_ERROR("Output:%s\n", buf); + return -1; + } + + return level; +} + +/* Get the nvidia gpu state */ +static int get_gpu_state_nv(struct GameModeGPUInfo *info) +{ + if (info->vendor != Vendor_NVIDIA) + return -1; + + if (!getenv("DISPLAY")) + LOG_ERROR("Getting Nvidia parameters requires DISPLAY to be set - will likely fail!\n"); + + long perf_level = get_max_perf_level_nv(info); + char arg[128] = { 0 }; char buf[EXTERNAL_BUFFER_MAX] = { 0 }; char *end; - if (info->nv_perf_level != -1) { - /* Get the GPUGraphicsClockOffset parameter */ - snprintf(arg, - 128, - NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, - info->device, - NV_CORE_OFFSET_ATTRIBUTE, - info->nv_perf_level); - const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; - if (run_external_process(exec_args_core, buf, -1) != 0) { - LOG_ERROR("Failed to get %s!\n", arg); - return -1; - } + /* Get the GPUGraphicsClockOffset parameter */ + snprintf(arg, + 128, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, + info->device, + NV_CORE_OFFSET_ATTRIBUTE, + perf_level); + const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; + if (run_external_process(exec_args_core, buf, -1) != 0) { + LOG_ERROR("Failed to get %s!\n", arg); + return -1; + } - info->nv_core = strtol(buf, &end, 10); - if (end == buf) { - LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); - return -1; - } + info->nv_core = strtol(buf, &end, 10); + if (end == buf) { + LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); + return -1; + } - /* Get the GPUMemoryTransferRateOffset parameter */ - snprintf(arg, - 128, - NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, - info->device, - NV_MEM_OFFSET_ATTRIBUTE, - info->nv_perf_level); - const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; - if (run_external_process(exec_args_mem, buf, -1) != 0) { - LOG_ERROR("Failed to get %s!\n", arg); - return -1; - } + /* Get the GPUMemoryTransferRateOffset parameter */ + snprintf(arg, + 128, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, + info->device, + NV_MEM_OFFSET_ATTRIBUTE, + perf_level); + const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; + if (run_external_process(exec_args_mem, buf, -1) != 0) { + LOG_ERROR("Failed to get %s!\n", arg); + return -1; + } - info->nv_mem = strtol(buf, &end, 10); - if (end == buf) { - LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); - return -1; - } + info->nv_mem = strtol(buf, &end, 10); + if (end == buf) { + LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); + return -1; } /* Get the GPUPowerMizerMode parameter */ @@ -135,6 +170,8 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) */ static int set_gpu_state_nv(struct GameModeGPUInfo *info) { + int status = 0; + if (info->vendor != Vendor_NVIDIA) return -1; @@ -143,35 +180,39 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) "Setting Nvidia parameters requires DISPLAY and XAUTHORITY to be set - will likely " "fail!\n"); + long perf_level = get_max_perf_level_nv(info); + char arg[128] = { 0 }; - if (info->nv_perf_level != -1) { - /* Set the GPUGraphicsClockOffset parameter */ + /* Set the GPUGraphicsClockOffset parameter */ + if (info->nv_core != -1) { snprintf(arg, 128, NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", info->device, NV_CORE_OFFSET_ATTRIBUTE, - info->nv_perf_level, + perf_level, info->nv_core); const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; if (run_external_process(exec_args_core, NULL, -1) != 0) { LOG_ERROR("Failed to set %s!\n", arg); - return -1; + status = -1; } + } - /* Set the GPUMemoryTransferRateOffset parameter */ + /* Set the GPUMemoryTransferRateOffset parameter */ + if (info->nv_mem != -1) { snprintf(arg, 128, NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", info->device, NV_MEM_OFFSET_ATTRIBUTE, - info->nv_perf_level, + perf_level, info->nv_mem); const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; if (run_external_process(exec_args_mem, NULL, -1) != 0) { LOG_ERROR("Failed to set %s!\n", arg); - return -1; + status = -1; } } @@ -186,11 +227,11 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) const char *exec_args_pm[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; if (run_external_process(exec_args_pm, NULL, -1) != 0) { LOG_ERROR("Failed to set %s!\n", arg); - return -1; + status = -1; } } - return 0; + return status; } /** @@ -313,7 +354,7 @@ static long get_generic_value(const char *val) */ int main(int argc, char *argv[]) { - if (argc >= 3 && strncmp(argv[2], "get", 3) == 0) { + if (argc == 3 && strncmp(argv[2], "get", 3) == 0) { /* Get and verify the vendor and device */ struct GameModeGPUInfo info; memset(&info, 0, sizeof(info)); @@ -323,13 +364,6 @@ int main(int argc, char *argv[]) /* Fetch the state and print it out */ switch (info.vendor) { case Vendor_NVIDIA: - if (argc < 3) { - LOG_ERROR("Must pass perf_level for nvidia gpu!\n"); - print_usage_and_exit(); - } - info.nv_perf_level = get_generic_value(argv[3]); - - /* Get nvidia info */ if (get_gpu_state_nv(&info) != 0) exit(EXIT_FAILURE); printf("%ld %ld %ld\n", info.nv_core, info.nv_mem, info.nv_powermizer_mode); @@ -354,18 +388,19 @@ int main(int argc, char *argv[]) switch (info.vendor) { case Vendor_NVIDIA: - if (argc < 5) { - LOG_ERROR("Must pass at least 5 arguments for nvidia gpu!\n"); + if (argc < 4) { + LOG_ERROR("Must pass at least 4 arguments for nvidia gpu!\n"); print_usage_and_exit(); } info.nv_core = get_generic_value(argv[3]); info.nv_mem = get_generic_value(argv[4]); - info.nv_perf_level = get_generic_value(argv[5]); /* Optional */ info.nv_powermizer_mode = -1; - if (argc >= 5) - info.nv_powermizer_mode = get_generic_value(argv[6]); + if (argc >= 6) + info.nv_powermizer_mode = get_generic_value(argv[5]); + + return set_gpu_state_nv(&info); break; case Vendor_AMD: if (argc < 3) { @@ -373,6 +408,7 @@ int main(int argc, char *argv[]) print_usage_and_exit(); } strncpy(info.amd_performance_level, argv[3], CONFIG_VALUE_MAX); + return set_gpu_state_amd(&info); break; default: LOG_ERROR("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", @@ -381,18 +417,6 @@ int main(int argc, char *argv[]) break; } - printf("gpuclockctl setting values on device:%ld with vendor 0x%04x", - info.device, - (unsigned short)info.vendor); - - switch (info.vendor) { - case Vendor_NVIDIA: - return set_gpu_state_nv(&info); - case Vendor_AMD: - return set_gpu_state_amd(&info); - default: - break; - } } else { print_usage_and_exit(); } diff --git a/example/gamemode.ini b/example/gamemode.ini index 99ee74b..b4a2d6e 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -53,11 +53,8 @@ inhibit_screensaver=1 ; See NV_CTRL_GPU_POWER_MIZER_MODE and friends in https://github.com/NVIDIA/nvidia-settings/blob/master/src/libXNVCtrl/NVCtrl.h ;nv_powermizer_mode=1 -; This corresponds to the performance level to edit in nvidia-xconfig -; Set this to the highest level shown in the nvidia settings power mizer settings -; Or from the command line check the highest perf value listed by "nvidia-settings -q [gpu:0]/GPUPerfModes" -;nv_perf_level=1 -; (these two are Mhz offsets from the baseline, ie. 0 applies no change) +; These will modify the core and mem clocks of the highest perf state in the Nvidia PowerMizer +; They are measured as Mhz offsets from the baseline, 0 will reset values to default, -1 or unset will not modify values ;nv_core_clock_mhz_offset=0 ;nv_mem_clock_mhz_offset=0 From dc1258d7501f1dac79193985a647e707dff4f282 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Mon, 11 Mar 2019 16:06:14 +0000 Subject: [PATCH 5/5] Automatically adjust the nvidia gpu index The drm device ID doesn't match up with the pci device as expected, spotted by issue #113 I can't test this myself just yet, will need data from the user in question to verify PCIDevice is the right value --- daemon/gpuclockctl.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index e50f5db..832b04e 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE. #define NV_MEM_OFFSET_ATTRIBUTE "GPUMemoryTransferRateOffset" #define NV_POWERMIZER_MODE_ATTRIBUTE "GPUPowerMizerMode" #define NV_PERFMODES_ATTRIBUTE "GPUPerfModes" +#define NV_PCIDEVICE_ATTRIBUTE "PCIDevice" #define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s" #define NV_PERF_LEVEL_FORMAT "[%ld]" @@ -63,6 +64,49 @@ static void print_usage_and_exit(void) exit(EXIT_FAILURE); } +/* Get the nvidia driver index for the current GPU */ +static long get_gpu_index_id_nv(struct GameModeGPUInfo *info) +{ + // Default to using the current device number + long gpu_index = info->device; + + if (info->vendor != Vendor_NVIDIA) + return -1; + + if (!getenv("DISPLAY")) + LOG_ERROR("Getting Nvidia parameters requires DISPLAY to be set - will likely fail!\n"); + long current = 0; + do { + char arg[128] = { 0 }; + char buf[EXTERNAL_BUFFER_MAX] = { 0 }; + char *end; + + /* Get the PCI id parameter */ + snprintf(arg, 128, NV_ATTRIBUTE_FORMAT, current, NV_PCIDEVICE_ATTRIBUTE); + const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; + if (run_external_process(exec_args_core, buf, -1) != 0) { + LOG_ERROR("Failed to get %s! Will be defaulting to nvidia gpu index %ld\n", + arg, + gpu_index); + /* Failure just means we've overrun the device list */ + break; + } + + long pcidevice = strtol(buf, &end, 10); + if (end == buf) { + LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); + break; + } + + if (info->device == pcidevice) { + gpu_index = current; + break; + } + } while (true); + + return gpu_index; +} + /* Get the max nvidia perf level */ static long get_max_perf_level_nv(struct GameModeGPUInfo *info) { @@ -361,9 +405,14 @@ int main(int argc, char *argv[]) info.device = get_device(argv[1]); info.vendor = gamemode_get_gpu_vendor(info.device); + /* Adjust the device number to the gpu index for Nvidia */ + if (info.vendor == Vendor_NVIDIA) + info.device = get_gpu_index_id_nv(&info); + /* Fetch the state and print it out */ switch (info.vendor) { case Vendor_NVIDIA: + if (get_gpu_state_nv(&info) != 0) exit(EXIT_FAILURE); printf("%ld %ld %ld\n", info.nv_core, info.nv_mem, info.nv_powermizer_mode);