소스 검색

Merge pull request #112 from mdiluz/automatic-gpu-info

Clean up NV optimization config
Alex Smith 6 년 전
부모
커밋
0c08359005
9개의 변경된 파일254개의 추가작업 그리고 156개의 파일을 삭제
  1. 2 5
      daemon/daemon_config.c
  2. 0 1
      daemon/daemon_config.h
  3. 3 54
      daemon/gamemode-gpu.c
  4. 5 1
      daemon/gamemode-tests.c
  5. 72 0
      daemon/gpu-control.c
  6. 4 2
      daemon/gpu-control.h
  7. 163 89
      daemon/gpuclockctl.c
  8. 1 0
      daemon/meson.build
  9. 4 4
      example/gamemode.ini

+ 2 - 5
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])

+ 0 - 1
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]);
 

+ 3 - 54
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;
 	}
 
@@ -118,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
@@ -139,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);
@@ -168,7 +129,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 +157,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);
 
@@ -205,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);
 
@@ -214,12 +171,10 @@ 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,
 		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,
 	};
@@ -238,21 +193,15 @@ 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];
-	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
 	const char *const exec_args[] = {
 		LIBEXECDIR "/gpuclockctl",
-		vendor,
 		device,
 		"get",
-		info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */
 		NULL,
 	};
 

+ 5 - 1
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();

+ 72 - 0
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 <stdio.h>
+
+/* 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;
+}

+ 4 - 2
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 */
@@ -55,3 +54,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);

+ 163 - 89
daemon/gpuclockctl.c

@@ -40,6 +40,8 @@ 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_PCIDEVICE_ATTRIBUTE "PCIDevice"
 #define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s"
 #define NV_PERF_LEVEL_FORMAT "[%ld]"
 
@@ -54,63 +56,140 @@ 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 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");
-
-	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);
+	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!\n", arg);
-			return -1;
+			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;
 		}
 
-		info->nv_core = strtol(buf, &end, 10);
+		long pcidevice = strtol(buf, &end, 10);
 		if (end == buf) {
 			LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf);
-			return -1;
+			break;
 		}
 
-		/* 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;
+		if (info->device == pcidevice) {
+			gpu_index = current;
+			break;
 		}
+	} while (true);
 
-		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;
-		}
+	return gpu_index;
+}
+
+/* Get the max nvidia perf level */
+static long get_max_perf_level_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");
+
+	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;
+
+	/* 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;
+	}
+
+	/* 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;
 	}
 
 	/* Get the GPUPowerMizerMode parameter */
@@ -135,6 +214,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 +224,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 +271,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;
 }
 
 /**
@@ -284,18 +369,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 +398,21 @@ 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);
+
+		/* 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:
-			info.nv_perf_level = -1;
-			if (argc > 4)
-				info.nv_perf_level = get_generic_value(argv[4]);
-			/* Get nvidia power level */
+
 			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,49 +423,49 @@ 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 < 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]);
+
+			/* Optional */
 			info.nv_powermizer_mode = -1;
-			if (argc > 7)
-				info.nv_powermizer_mode = get_generic_value(argv[7]);
-			break;
-		case Vendor_AMD:
-			strncpy(info.amd_performance_level, argv[4], CONFIG_VALUE_MAX);
-			break;
-		default:
-			printf("Currently unsupported GPU vendor 0x%04x, doing nothing!\n", (short)info.vendor);
-			break;
-		}
+			if (argc >= 6)
+				info.nv_powermizer_mode = get_generic_value(argv[5]);
 
-		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);
+			break;
 		case Vendor_AMD:
+			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);
 			return set_gpu_state_amd(&info);
+			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;
 		}
+
 	} else {
 		print_usage_and_exit();
 	}

+ 1 - 0
daemon/meson.build

@@ -3,6 +3,7 @@ common_sources = [
     'logging.c',
     'governors-query.c',
     'external-helper.c',
+    'gpu-control.c',
 ]
 
 daemon_common = static_library(

+ 4 - 4
example/gamemode.ini

@@ -49,12 +49,12 @@ 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)
-;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