From 0deb9a2acad5e2f6bfdcd6b2938a43076554ef93 Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Tue, 21 Jun 2016 03:00:51 +0200 Subject: [PATCH] win32: implement a nvapi.dll wrapper like nvml Allow to get/set missing infos like the power limit on x86 squashed for a better min/max and device mapping Signed-off-by: Tanguy Pruvot --- Algo256/bmw.cu | 5 +- ccminer.cpp | 5 +- ccminer.vcxproj | 3 +- ccminer.vcxproj.filters | 5 +- compat/nvapi/nvapi_ccminer.h | 46 ++++++++++++- nvapi.cpp | 128 +++++++++++++++++++++++++++++++++++ nvml.cpp | 73 ++++++++++++++++++-- 7 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 nvapi.cpp diff --git a/Algo256/bmw.cu b/Algo256/bmw.cu index fc05db2..2bef9ca 100644 --- a/Algo256/bmw.cu +++ b/Algo256/bmw.cu @@ -6,9 +6,8 @@ extern "C" { #include "sph/sph_bmw.h" } -#include "miner.h" - -#include "cuda_helper.h" +#include +#include static uint32_t *d_hash[MAX_GPUS]; diff --git a/ccminer.cpp b/ccminer.cpp index ba0089d..3aa82e7 100644 --- a/ccminer.cpp +++ b/ccminer.cpp @@ -295,12 +295,15 @@ Options:\n\ --max-rate=N[KMG] Only mine if net hashrate is less than specified value\n\ --max-diff=N Only mine if net difficulty is less than specified value\n\ Can be tuned with --resume-diff=N to set a resume value\n" -#if defined(USE_WRAPNVML) && (defined(__linux) || defined(_WIN64)) /* via nvml */ +#if defined(__linux) || defined(_WIN64) /* via nvml */ "\ --mem-clock=3505 Set the gpu memory max clock (346.72+ driver)\n\ --gpu-clock=1150 Set the gpu engine max clock (346.72+ driver)\n\ --pstate=0[,2] Set the gpu power state (352.21+ driver)\n\ --plimit=100W Set the gpu power limit (352.21+ driver)\n" +#else /* via nvapi.dll */ +"\ + --plimit=100 Set the gpu power limit in percentage\n" #endif #ifdef HAVE_SYSLOG_H "\ diff --git a/ccminer.vcxproj b/ccminer.vcxproj index 34453bd..cc50369 100644 --- a/ccminer.vcxproj +++ b/ccminer.vcxproj @@ -230,6 +230,7 @@ false Full + @@ -539,4 +540,4 @@ - + \ No newline at end of file diff --git a/ccminer.vcxproj.filters b/ccminer.vcxproj.filters index 0a5fbac..5decd0d 100644 --- a/ccminer.vcxproj.filters +++ b/ccminer.vcxproj.filters @@ -264,6 +264,9 @@ Source Files\sph + + Source Files + @@ -734,4 +737,4 @@ Ressources - + \ No newline at end of file diff --git a/compat/nvapi/nvapi_ccminer.h b/compat/nvapi/nvapi_ccminer.h index db4525c..fda480a 100644 --- a/compat/nvapi/nvapi_ccminer.h +++ b/compat/nvapi/nvapi_ccminer.h @@ -1,2 +1,44 @@ -/* todo: stripped version... or not */ -#include "nvapi.h" \ No newline at end of file +#pragma once + +#include "nvapi.h" + +NvAPI_Status nvapi_dll_init(); + +typedef struct { + NvU32 version; + NvU32 flags; + struct + { + NvU32 pstate; // Assumption + NvU32 unknown1[2]; + NvU32 min_power; + NvU32 unknown2[2]; + NvU32 def_power; + NvU32 unknown3[2]; + NvU32 max_power; + NvU32 unknown4; // 0 + } entries[4]; +} NVAPI_GPU_POWER_INFO; + +typedef struct { + NvU32 version; + NvU32 flags; + struct { + NvU32 unknown1; + NvU32 unknown2; + NvU32 power; // percent * 1000 + NvU32 unknown4; + } entries[4]; +} NVAPI_GPU_POWER_STATUS; + +#define NVAPI_GPU_POWER_STATUS_VER MAKE_NVAPI_VERSION(NVAPI_GPU_POWER_STATUS, 1) +#define NVAPI_GPU_POWER_INFO_VER MAKE_NVAPI_VERSION(NVAPI_GPU_POWER_INFO, 1) + +NvAPI_Status NvAPI_DLL_GetInterfaceVersionString(NvAPI_ShortString string); +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesGetInfo(NvPhysicalGpuHandle hPhysicalGpu, NVAPI_GPU_POWER_INFO* pInfo); +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesGetStatus(NvPhysicalGpuHandle hPhysicalGpu, NVAPI_GPU_POWER_STATUS* pPolicies); +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesSetStatus(NvPhysicalGpuHandle hPhysicalGpu, NVAPI_GPU_POWER_STATUS* pPolicies); + +NvAPI_Status NvAPI_DLL_Unload(); + +#define NV_ASSERT(x) { NvAPI_Status ret = x; if(ret != NVAPI_OK) return ret; } diff --git a/nvapi.cpp b/nvapi.cpp new file mode 100644 index 0000000..2e86d29 --- /dev/null +++ b/nvapi.cpp @@ -0,0 +1,128 @@ +/** + * Wrapper to nvapi.dll to query informations missing for x86 binaries (there is no nvml x86) + * based on the work of https://github.com/ircubic/lib_gpu + * + * tpruvot@ccminer.org 06-2016 + */ + +#ifdef _WIN32 + +#include +#include +#include + +#include "compat/nvapi/nvapi_ccminer.h" + +class NvAPILibraryHandle +{ + typedef void *(*QueryPtr)(uint32_t); + +private: + HMODULE library; + QueryPtr nvidia_query; + +public: + NvAPILibraryHandle() + { + bool success = false; + library = LoadLibrary("nvapi.dll"); + if (library != NULL) { + nvidia_query = reinterpret_cast(GetProcAddress(library, "nvapi_QueryInterface")); + if (nvidia_query != NULL) { + const uint32_t NVAPI_ID_INIT = 0x0150E828; + auto init = static_cast(nvidia_query(NVAPI_ID_INIT)); + NvAPI_Status ret = init(); + success = (ret == NVAPI_OK); + } + } + + if (!success) { + throw std::runtime_error("Unable to locate NVAPI library!"); + } + } + + ~NvAPILibraryHandle() + { + NvAPI_DLL_Unload(); + FreeLibrary(library); + } + + void *query(uint32_t ID) + { + return nvidia_query(ID); + } + +}; + +static std::unique_ptr nvidia_handle; +bool nvapi_dll_loaded = false; + +NvAPI_Status nvapi_dll_init() +{ + try { + if (!nvapi_dll_loaded) { + nvidia_handle = std::make_unique(); + nvapi_dll_loaded = true; + } + } + catch (std::runtime_error) { + nvapi_dll_loaded = false; + return NVAPI_ERROR; + } + + return NVAPI_OK; +} + +// Hidden nvapi.dll functions + +#define NVAPI_ID_IFVERSION 0x01053FA5 +NvAPI_Status NvAPI_DLL_GetInterfaceVersionString(NvAPI_ShortString string) { + static NvAPI_Status (*pointer)(NvAPI_ShortString string) = NULL; + if(!nvapi_dll_loaded) return NVAPI_API_NOT_INITIALIZED; + if(!pointer) { + pointer = (NvAPI_Status (*)(NvAPI_ShortString))nvidia_handle->query(NVAPI_ID_IFVERSION); + } + return (*pointer)(string); +} + +#define NVAPI_ID_POWER_INFO 0x34206D86 +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesGetInfo(NvPhysicalGpuHandle handle, NVAPI_GPU_POWER_INFO* pInfo) { + static NvAPI_Status (*pointer)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_INFO*) = NULL; + if(!nvapi_dll_loaded) return NVAPI_API_NOT_INITIALIZED; + if(!pointer) { + pointer = (NvAPI_Status (*)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_INFO*))nvidia_handle->query(NVAPI_ID_POWER_INFO); + } + return (*pointer)(handle, pInfo); +} + +#define NVAPI_ID_POWERPOL_GET 0x70916171 +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesGetStatus(NvPhysicalGpuHandle handle, NVAPI_GPU_POWER_STATUS* pPolicies) { + static NvAPI_Status (*pointer)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_STATUS*) = NULL; + if(!nvapi_dll_loaded) return NVAPI_API_NOT_INITIALIZED; + if(!pointer) { + pointer = (NvAPI_Status (*)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_STATUS*))nvidia_handle->query(NVAPI_ID_POWERPOL_GET); + } + return (*pointer)(handle, pPolicies); +} + +#define NVAPI_ID_POWERPOL_SET 0x0AD95F5ED +NvAPI_Status NvAPI_DLL_ClientPowerPoliciesSetStatus(NvPhysicalGpuHandle handle, NVAPI_GPU_POWER_STATUS* pPolicies) { + static NvAPI_Status (*pointer)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_STATUS*) = NULL; + if(!nvapi_dll_loaded) return NVAPI_API_NOT_INITIALIZED; + if(!pointer) { + pointer = (NvAPI_Status (*)(NvPhysicalGpuHandle, NVAPI_GPU_POWER_STATUS*))nvidia_handle->query(NVAPI_ID_POWERPOL_SET); + } + return (*pointer)(handle, pPolicies); +} + +#define NVAPI_ID_UNLOAD 0xD22BDD7E +NvAPI_Status NvAPI_DLL_Unload() { + static NvAPI_Status (*pointer)() = NULL; + if(!nvapi_dll_loaded) return NVAPI_API_NOT_INITIALIZED; + if(!pointer) { + pointer = (NvAPI_Status (*)())nvidia_handle->query(NVAPI_ID_UNLOAD); + } + return (*pointer)(); +} + +#endif \ No newline at end of file diff --git a/nvml.cpp b/nvml.cpp index 0063dab..c75cd59 100644 --- a/nvml.cpp +++ b/nvml.cpp @@ -460,7 +460,7 @@ int nvml_set_pstate(nvml_handle *nvmlh, int dev_id) nclocks = min(nclocks, 32); if (nclocks) nvmlh->nvmlDeviceGetSupportedMemoryClocks(nvmlh->devs[n], &nclocks, mem_clocks); - if (wanted_pstate+1 > nclocks) { + if ((uint32_t) wanted_pstate+1 > nclocks) { applog(LOG_WARNING, "GPU #%d: only %u mem clocks available (p-states)", dev_id, nclocks); } for (uint8_t u=0; u < nclocks; u++) { @@ -789,6 +789,8 @@ int nvml_destroy(nvml_handle *nvmlh) return 0; } +// ---------------------------------------------------------------------------- + /** * nvapi alternative for windows x86 binaries * nvml api doesn't exists as 32bit dll :/// @@ -800,6 +802,7 @@ static int nvapi_dev_map[MAX_GPUS] = { 0 }; static NvDisplayHandle hDisplay_a[NVAPI_MAX_PHYSICAL_GPUS * 2] = { 0 }; static NvPhysicalGpuHandle phys[NVAPI_MAX_PHYSICAL_GPUS] = { 0 }; static NvU32 nvapi_dev_cnt = 0; +extern bool nvapi_dll_loaded; int nvapi_temperature(unsigned int devNum, unsigned int *temperature) { @@ -967,6 +970,52 @@ int nvapi_getbios(unsigned int devNum, char *desc, unsigned int maxlen) return 0; } +uint8_t nvapi_getplimit(unsigned int devNum) +{ + NvAPI_Status ret = NVAPI_OK; + NVAPI_GPU_POWER_STATUS pol = { 0 }; + pol.version = NVAPI_GPU_POWER_STATUS_VER; + if ((ret = NvAPI_DLL_ClientPowerPoliciesGetStatus(phys[devNum], &pol)) != NVAPI_OK) { + NvAPI_ShortString string; + NvAPI_GetErrorMessage(ret, string); + if (opt_debug) + applog(LOG_DEBUG, "NVAPI GetPowerPoliciesStatus: %s", string); + return 0; + } + return (uint8_t) (pol.entries[0].power / 1000); // in percent +} + +int nvapi_setplimit(unsigned int devNum, uint16_t percent) +{ + NvAPI_Status ret = NVAPI_OK; + uint32_t val = percent * 1000; + + NVAPI_GPU_POWER_INFO nfo = { 0 }; + nfo.version = NVAPI_GPU_POWER_INFO_VER; + ret = NvAPI_DLL_ClientPowerPoliciesGetInfo(phys[devNum], &nfo); + if (ret == NVAPI_OK) { + if (val == 0) + val = nfo.entries[0].def_power; + else if (val < nfo.entries[0].min_power) + val = nfo.entries[0].min_power; + else if (val > nfo.entries[0].max_power) + val = nfo.entries[0].max_power; + } + + NVAPI_GPU_POWER_STATUS pol = { 0 }; + pol.version = NVAPI_GPU_POWER_STATUS_VER; + pol.flags = 1; + pol.entries[0].power = val; + if ((ret = NvAPI_DLL_ClientPowerPoliciesSetStatus(phys[devNum], &pol)) != NVAPI_OK) { + NvAPI_ShortString string; + NvAPI_GetErrorMessage(ret, string); + if (opt_debug) + applog(LOG_DEBUG, "NVAPI SetPowerPoliciesStatus: %s", string); + return -1; + } + return ret; +} + int nvapi_init() { int num_gpus = cuda_num_devices(); @@ -1017,11 +1066,12 @@ int nvapi_init() applog(LOG_DEBUG, "NVAPI NvAPI_GPU_GetFullName: %s", string); } } - #if 0 - NvAPI_ShortString ver; - NvAPI_GetInterfaceVersionString(ver); - applog(LOG_DEBUG, "NVAPI Version: %s", ver); + if (opt_debug) { + NvAPI_ShortString ver; + NvAPI_GetInterfaceVersionString(ver); + applog(LOG_DEBUG, "%s", ver); + } #endif NvU32 udv; @@ -1031,6 +1081,19 @@ int nvapi_init() sprintf(driver_version,"%d.%02d", udv / 100, udv % 100); } + // nvapi.dll + ret = nvapi_dll_init(); + if (ret == NVAPI_OK) { + for (int n=0; n < opt_n_threads; n++) { + int dev_id = device_map[n % MAX_GPUS]; + if (device_plimit[dev_id]) { + nvapi_setplimit(nvapi_dev_map[dev_id], device_plimit[dev_id]); // 0=default + uint32_t res = nvapi_getplimit(nvapi_dev_map[dev_id]); + gpulog(LOG_INFO, n, "NVAPI power limit is set to %u%%", res); + } + } + } + return 0; } #endif