/* -*- mode: c++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
* vim: ts=4 sw=4 noet ai cindent syntax=cpp
*
* Conky, a system monitor, based on torsmo
*
* Any original torsmo code is licensed under the BSD license
*
* All code written since the fork of torsmo is licensed under the GPL
*
* Please see COPYING for details
*
* Copyright (c) 2008 Markus Meissner
* Copyright (c) 2005-2012 Brenden Matthews, Philip Kovacs, et. al.
* (see AUTHORS)
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
/*
*
* Author:
* Fonic
*
* TODO:
* - Move decoding of GPU/MEM freqs to print_nvidia_value() using QUERY_SPECIAL
* so that all quirks are located there
* - Implement nvs->print_type to allow control over how the value is printed
* (int, float, temperature...)
*
* Showcase (conky.conf):
* --==| NVIDIA | ==--
* GPU ${nvidia gpufreq [gpu_id]}MHz (${nvidia gpufreqmin [gpu_id]}-${nvidia gpufreqmax [gpu_id]}MHz)
* MEM ${nvidia memfreq [gpu_id]}MHz (${nvidia memfreqmin [gpu_id]}-${nvidia memfreqmax [gpu_id]}MHz)
* MTR ${nvidia mtrfreq [gpu_id]}MHz (${nvidia mtrfreqmin [gpu_id]}-${nvidia mtrfreqmax [gpu_id]}MHz)
* PERF Level ${nvidia perflevel [gpu_id]} (${nvidia perflevelmin [gpu_id]}-${nvidia perflevelmax [gpu_id]}), Mode: ${nvidia perfmode [gpu_id]}
* VRAM ${nvidia memutil [gpu_id]}% (${nvidia memused [gpu_id]}MB/${nvidia memtotal [gpu_id]}MB)
* LOAD GPU ${nvidia gpuutil [gpu_id]}%, RAM ${nvidia membwutil [gpu_id]}%, VIDEO ${nvidia videoutil [gpu_id]}%, PCIe ${nvidia pcieutil [gpu_id]}%
* TEMP GPU ${nvidia gputemp [gpu_id]}°C (${nvidia gputempthreshold [gpu_id]}°C max.), SYS ${nvidia ambienttemp [gpu_id]}°C
* FAN ${nvidia fanspeed [gpu_id]} RPM (${nvidia fanlevel [gpu_id]}%)
*
* miscelaneas:
* OPENGL ${nvidia imagequality [gpu_id]}
* GPU ${nvidia modelname [gpu_id]}
*
* --==| NVIDIA Bars |==--
* LOAD ${nvidiabar [height][,width] gpuutil [gpu_id]}
* VRAM ${nvidiabar [height][,width] memutil [gpu_id]}
* RAM ${nvidiabar [height][,width] membwutil [gpu_id]}
* VIDEO ${nvidiabar [height][,width] videoutil [gpu_id]}
* PCIe ${nvidiabar [height][,width] pcieutil [gpu_id]}
* Fan ${nvidiabar [height][,width] fanlevel [gpu_id]}
* TEMP ${nvidiabar [height][,width] gputemp [gpu_id]}
*
* --==| NVIDIA Gauge |==--
* LOAD ${nvidiagauge [height][,width] gpuutil [gpu_id]}
* VRAM ${nvidiagauge [height][,width] memutil [gpu_id]}
* RAM ${nvidiagauge [height][,width] membwutil [gpu_id]}
* VIDEO ${nvidiagauge [height][,width] videoutil [gpu_id]}
* PCIe ${nvidiagauge [height][,width] pcieutil [gpu_id]}
* Fan ${nvidiagauge [height][,width] fanlevel [gpu_id]}
* TEMP ${nvidiagauge [height][,width] gputemp [gpu_id]}
*
* --==| NVIDIA Graph |==-- (gpu_id is not optional in this case)
* LOAD ${nvidiagraph gpuutil [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* VRAM ${nvidiagraph memutil [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* RAM ${nvidiagraph membwutil [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* VIDEO ${nvidiagraph videoutil [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* PCIe ${nvidiagraph pcieutil [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* Fan ${nvidiagraph fanlevel [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
* TEMP ${nvidiagraph gputemp [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l] gpu_id}
*/
#include "conky.h"
#include "logging.h"
#include "nvidia.h"
#include "temphelper.h"
#include "x11.h"
#include
// Separators for nvidia string parsing
// (sample: "perf=0, nvclock=324, nvclockmin=324, nvclockmax=324 ; perf=1, nvclock=549, nvclockmin=549, nvclockmax=549")
#define NV_KVPAIR_SEPARATORS ", ;"
#define NV_KEYVAL_SEPARATORS "="
// Module arguments
const char* translate_module_argument[] = {
"temp", // Temperatures
"gputemp",
"threshold",
"gputempthreshold",
"ambient",
"ambienttemp",
"gpufreq", // GPU frequency
"gpufreqcur",
"gpufreqmin",
"gpufreqmax",
"memfreq", // Memory frequency
"memfreqcur",
"memfreqmin",
"memfreqmax",
"mtrfreq", // Memory transfer rate frequency
"mtrfreqcur",
"mtrfreqmin",
"mtrfreqmax",
"perflevel", // Performance levels
"perflevelcur",
"perflevelmin",
"perflevelmax",
"perfmode",
"gpuutil", // Load/utilization
"membwutil", // NOTE: this is the memory _bandwidth_ utilization, not the percentage of used/available memory!
"videoutil",
"pcieutil",
"mem", // RAM statistics
"memused",
"memfree",
"memavail",
"memmax",
"memtotal",
"memutil",
"memperc",
"fanspeed", // Fan/cooler
"fanlevel",
"imagequality", // Miscellaneous
"modelname"
};
// Enum for module arguments
typedef enum _ARG_ID {
ARG_TEMP,
ARG_GPU_TEMP,
ARG_THRESHOLD,
ARG_GPU_TEMP_THRESHOLD,
ARG_AMBIENT,
ARG_AMBIENT_TEMP,
ARG_GPU_FREQ,
ARG_GPU_FREQ_CUR,
ARG_GPU_FREQ_MIN,
ARG_GPU_FREQ_MAX,
ARG_MEM_FREQ,
ARG_MEM_FREQ_CUR,
ARG_MEM_FREQ_MIN,
ARG_MEM_FREQ_MAX,
ARG_MTR_FREQ,
ARG_MTR_FREQ_CUR,
ARG_MTR_FREQ_MIN,
ARG_MTR_FREQ_MAX,
ARG_PERF_LEVEL,
ARG_PERF_LEVEL_CUR,
ARG_PERF_LEVEL_MIN,
ARG_PERF_LEVEL_MAX,
ARG_PERF_MODE,
ARG_GPU_UTIL,
ARG_MEM_BW_UTIL,
ARG_VIDEO_UTIL,
ARG_PCIE_UTIL,
ARG_MEM,
ARG_MEM_USED,
ARG_MEM_FREE,
ARG_MEM_AVAIL,
ARG_MEM_MAX,
ARG_MEM_TOTAL,
ARG_MEM_UTIL,
ARG_MEM_PERC,
ARG_FAN_SPEED,
ARG_FAN_LEVEL,
ARG_IMAGEQUALITY,
ARG_MODEL_NAME,
ARG_UNKNOWN
} ARG_ID;
// Nvidia query targets
const int translate_nvidia_target[] = {
NV_CTRL_TARGET_TYPE_X_SCREEN,
NV_CTRL_TARGET_TYPE_GPU,
NV_CTRL_TARGET_TYPE_FRAMELOCK,
NV_CTRL_TARGET_TYPE_VCSC,
NV_CTRL_TARGET_TYPE_GVI,
NV_CTRL_TARGET_TYPE_COOLER,
NV_CTRL_TARGET_TYPE_THERMAL_SENSOR,
NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER,
NV_CTRL_TARGET_TYPE_DISPLAY,
};
// Enum for nvidia query targets
typedef enum _TARGET_ID {
TARGET_SCREEN,
TARGET_GPU,
TARGET_FRAMELOCK,
TARGET_VCSC,
TARGET_GVI,
TARGET_COOLER,
TARGET_THERMAL,
TARGET_3DVISION,
TARGET_DISPLAY
} TARGET_ID;
// Nvidia query attributes
const int translate_nvidia_attribute[] = {
NV_CTRL_GPU_CORE_TEMPERATURE,
NV_CTRL_GPU_CORE_THRESHOLD,
NV_CTRL_AMBIENT_TEMPERATURE,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_STRING_PERFORMANCE_MODES,
NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_GPU_POWER_MIZER_MODE,
NV_CTRL_STRING_GPU_UTILIZATION,
NV_CTRL_USED_DEDICATED_GPU_MEMORY,
0,
NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY, // NOTE: NV_CTRL_TOTAL_GPU_MEMORY would be better, but returns KB instead of MB
0,
NV_CTRL_THERMAL_COOLER_SPEED,
NV_CTRL_THERMAL_COOLER_LEVEL,
NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL,
NV_CTRL_IMAGE_SETTINGS,
};
// Enum for nvidia query attributes
typedef enum _ATTR_ID {
ATTR_GPU_TEMP,
ATTR_GPU_TEMP_THRESHOLD,
ATTR_AMBIENT_TEMP,
ATTR_GPU_FREQ,
ATTR_MEM_FREQ,
ATTR_PERFMODES_STRING,
ATTR_FREQS_STRING,
ATTR_PERF_MODE,
ATTR_UTILS_STRING,
ATTR_MEM_USED,
ATTR_MEM_FREE,
ATTR_MEM_TOTAL,
ATTR_MEM_UTIL,
ATTR_FAN_SPEED,
ATTR_FAN_LEVEL,
ATTR_PERF_LEVEL,
ATTR_IMAGE_QUALITY,
ATTR_MODEL_NAME,
} ATTR_ID;
// Enum for query type
typedef enum _QUERY_ID {
QUERY_VALUE,
QUERY_STRING,
QUERY_STRING_VALUE,
QUERY_SPECIAL
} QUERY_ID;
// Enum for string token search mode
typedef enum _SEARCH_ID {
SEARCH_FIRST,
SEARCH_LAST,
SEARCH_MIN,
SEARCH_MAX
} SEARCH_ID;
// Translate special_type into command string
const char* translate_nvidia_special_type[] = {
"nvidia", // NONSPECIAL
"", // HORIZONTAL_LINE
"", // STIPPLED_HR
"nvidiabar", // BAR
"", // FG
"", // BG
"", // OUTLINE
"", // ALIGNR
"", // ALIGNC
"nvidiagague", // GAUGE
"nvidiagraph", // GRAPH
"", // OFFSET
"", // VOFFSET
"", // FONT
"", // GOTO
"" // TAB
};
// Global struct to keep track of queries
struct nvidia_s {
const char *command;
const char *arg;
QUERY_ID query;
TARGET_ID target;
ATTR_ID attribute;
char *token;
SEARCH_ID search;
// added new field for GPU id
int gpu_id;
};
//Cache by value
struct nvidia_c_value {
int memtotal = -1;
int gputempthreshold = -1;
};
//Cache by string
struct nvidia_c_string {
int nvclockmin = -1;
int nvclockmax = -1;
int memclockmin = -1;
int memclockmax = -1;
int memTransferRatemin = -1;
int memTransferRatemax = -1;
int perfmin = -1;
int perfmax = -1;
};
static Display *nvdisplay;
// Maximum number of GPU connected:
// For cache default value: choosed a model of direct access to array instead of list for speed improvement
// value based on the incoming quad Naples tech having 256 PCIe lanes available
const int MAXNUMGPU=64;
namespace {
class nvidia_display_setting: public conky::simple_config_setting {
typedef conky::simple_config_setting Base;
protected:
virtual void lua_setter(lua::state &l, bool init);
virtual void cleanup(lua::state &l);
public:
nvidia_display_setting()
: Base("nvidia_display", std::string(), false)
{}
};
void nvidia_display_setting::lua_setter(lua::state &l, bool init)
{
lua::stack_sentry s(l, -2);
Base::lua_setter(l, init);
std::string str = do_convert(l, -1).first;
if(str.size()) {
if ((nvdisplay = XOpenDisplay(str.c_str())) == NULL) {
CRIT_ERR(NULL, NULL, "can't open nvidia display: %s", XDisplayName(str.c_str()));
}
}
++s;
}
void nvidia_display_setting::cleanup(lua::state &l)
{
lua::stack_sentry s(l, -1);
if(nvdisplay) {
XCloseDisplay(nvdisplay);
nvdisplay = NULL;
}
l.pop();
}
nvidia_display_setting nvidia_display;
}
// Evaluate module parameters and prepare query
int set_nvidia_query(struct text_object *obj, const char *arg, unsigned int special_type)
{
struct nvidia_s *nvs;
int aid;
int ilen;
// Initialize global struct
obj->data.opaque = malloc(sizeof(struct nvidia_s));
nvs = static_cast(obj->data.opaque);
memset(nvs, 0, sizeof(struct nvidia_s));
// Added new parameter parsing GPU_ID as 0,1,2,..
// if no GPU_ID parameter then default to 0
nvs->gpu_id = 0;
char *strbuf = strdup(arg);
char *p = strrchr(strbuf, ' ');
if (p && *(p + 1)) {
nvs->gpu_id = atoi(p+1);
if( (nvs->gpu_id > 0) || !strcmp(p+1,"0") ) {
ilen = strlen(strbuf);
ilen = ilen - strlen(p);
strbuf[ilen] = 0;
arg = strbuf;
}
}
// If the value is negative it is set to 0
if (nvs->gpu_id < 0) nvs->gpu_id = 0;
// Extract arguments for nvidiabar, etc, and run set_nvidia_query
switch (special_type) {
case BAR:
arg = scan_bar(obj, arg, 100);
break;
case GRAPH:
arg = scan_graph(obj, arg, 100);
break;
case GAUGE:
arg = scan_gauge(obj, arg, 100);
break;
}
// Return error if no argument
// (sometimes scan_graph gets excited and eats the whole string!
if (!arg) return 1;
// Translate parameter to id
for (aid=0; aid < ARG_UNKNOWN; aid++) {
if (strcmp(arg, translate_module_argument[aid]) == 0)
break;
}
// free the string buffer after arg is not anymore needed
if (strbuf != NULL) free(strbuf);
// Save pointers to the arg and command strings for debugging and printing
nvs->arg = translate_module_argument[aid];
nvs->command = translate_nvidia_special_type[special_type];
// Evaluate parameter
switch(aid) {
case ARG_TEMP: // GPU temperature
case ARG_GPU_TEMP:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_TEMP;
break;
case ARG_THRESHOLD: // GPU temperature threshold
case ARG_GPU_TEMP_THRESHOLD:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_TEMP_THRESHOLD;
break;
case ARG_AMBIENT: // Ambient temperature
case ARG_AMBIENT_TEMP:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_AMBIENT_TEMP;
break;
case ARG_GPU_FREQ: // Current GPU clock
case ARG_GPU_FREQ_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_FREQ;
break;
case ARG_GPU_FREQ_MIN: // Minimum GPU clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "nvclockmin";
nvs->search = SEARCH_MIN;
break;
case ARG_GPU_FREQ_MAX: // Maximum GPU clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "nvclockmax";
nvs->search = SEARCH_MAX;
break;
case ARG_MEM_FREQ: // Current memory clock
case ARG_MEM_FREQ_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_FREQ;
break;
case ARG_MEM_FREQ_MIN: // Minimum memory clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memclockmin";
nvs->search = SEARCH_MIN;
break;
case ARG_MEM_FREQ_MAX: // Maximum memory clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memclockmax";
nvs->search = SEARCH_MAX;
break;
case ARG_MTR_FREQ: // Current memory transfer rate clock
case ARG_MTR_FREQ_CUR:
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_FREQS_STRING;
nvs->token = (char*) "memTransferRate";
nvs->search = SEARCH_FIRST;
break;
case ARG_MTR_FREQ_MIN: // Minimum memory transfer rate clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memTransferRatemin";
nvs->search = SEARCH_MIN;
break;
case ARG_MTR_FREQ_MAX: // Maximum memory transfer rate clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memTransferRatemax";
nvs->search = SEARCH_MAX;
break;
case ARG_PERF_LEVEL: // Current performance level
case ARG_PERF_LEVEL_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERF_LEVEL;
break;
case ARG_PERF_LEVEL_MIN: // Lowest performance level
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "perf";
nvs->search = SEARCH_MIN;
break;
case ARG_PERF_LEVEL_MAX: // Highest performance level
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "perf";
nvs->search = SEARCH_MAX;
break;
case ARG_PERF_MODE: // Performance mode
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERF_MODE;
break;
case ARG_GPU_UTIL: // GPU utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "graphics";
nvs->search = SEARCH_FIRST;
break;
case ARG_MEM_BW_UTIL: // Memory bandwidth utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "memory";
nvs->search = SEARCH_FIRST;
break;
case ARG_VIDEO_UTIL: // Video engine utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "video";
nvs->search = SEARCH_FIRST;
break;
case ARG_PCIE_UTIL: // PCIe bandwidth utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "PCIe";
nvs->search = SEARCH_FIRST;
break;
case ARG_MEM: // Amount of used memory
case ARG_MEM_USED:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_USED;
break;
case ARG_MEM_FREE: // Amount of free memory
case ARG_MEM_AVAIL:
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_FREE;
break;
case ARG_MEM_MAX: // Total amount of memory
case ARG_MEM_TOTAL:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_TOTAL;
break;
case ARG_MEM_UTIL: // Memory utilization %
case ARG_MEM_PERC:
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_UTIL;
break;
case ARG_FAN_SPEED: // Fan speed
nvs->query = QUERY_VALUE;
nvs->target = TARGET_COOLER;
nvs->attribute = ATTR_FAN_SPEED;
break;
case ARG_FAN_LEVEL: // Fan level %
nvs->query = QUERY_VALUE;
nvs->target = TARGET_COOLER;
nvs->attribute = ATTR_FAN_LEVEL;
break;
case ARG_IMAGEQUALITY: // Image quality
nvs->query = QUERY_VALUE;
nvs->target = TARGET_SCREEN;
nvs->attribute = ATTR_IMAGE_QUALITY;
break;
case ARG_MODEL_NAME:
nvs->query = QUERY_STRING;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MODEL_NAME;
break;
default: // Unknown/invalid argument
// Error printed by core.cc
return 1;
}
return 0;
}
// Return the amount of targets present or raise error)
static inline int get_nvidia_target_count(Display *dpy, TARGET_ID tid)
{
int num_tgts;
if (!XNVCTRLQueryTargetCount(dpy, translate_nvidia_target[tid], &num_tgts)) {
num_tgts = -1;
}
if(num_tgts < 1) {
// Print error and exit if there's no NVIDIA's GPU
CRIT_ERR(NULL, NULL, "%s:"
"\n Trying to query Nvidia target failed (using the propietary drivers)."
"\n Are you sure they are installed correctly and a Nvidia GPU is in use?"
"\n (display: %d,Nvidia target_count: %d)"
, __func__, dpy, num_tgts );
}
return num_tgts;
}
static int cache_nvidia_value(TARGET_ID tid, ATTR_ID aid, Display *dpy, int *value, int gid)
{
static nvidia_c_value ac_value[MAXNUMGPU];
if (aid == ATTR_MEM_TOTAL) {
if (ac_value[gid].memtotal < 0) {
if(!dpy || !XNVCTRLQueryTargetAttribute(dpy, translate_nvidia_target[tid], gid, 0, translate_nvidia_attribute[aid], value)){
NORM_ERR("%s: Something went wrong running nvidia query (tid: %d, aid: %d)", __func__, tid, aid);
return -1;
}
ac_value[gid].memtotal = *value;
} else {
*value = ac_value[gid].memtotal;
}
} else if (aid == ATTR_GPU_TEMP_THRESHOLD) {
if (ac_value[gid].gputempthreshold < 0) {
if(!dpy || !XNVCTRLQueryTargetAttribute(dpy, translate_nvidia_target[tid], gid, 0, translate_nvidia_attribute[aid], value)){
NORM_ERR("%s: Something went wrong running nvidia query (tid: %d, aid: %d)", __func__, tid, aid);
return -1;
}
ac_value[gid].gputempthreshold = *value;
} else {
*value = ac_value[gid].gputempthreshold;
}
}
return 0;
}
// Retrieve attribute value via nvidia interface
static int get_nvidia_value(TARGET_ID tid, ATTR_ID aid, int gid)
{
Display *dpy = nvdisplay ? nvdisplay : display;
int value;
// Check if the aid is cacheable
if (aid == ATTR_MEM_TOTAL || aid == ATTR_GPU_TEMP_THRESHOLD) {
if (cache_nvidia_value(tid, aid, dpy, &value, gid)) {
return -1;
}
// If not, then query it
} else {
if(!dpy || !XNVCTRLQueryTargetAttribute(dpy, translate_nvidia_target[tid], gid, 0, translate_nvidia_attribute[aid], &value)){
NORM_ERR("%s: Something went wrong running nvidia query (tid: %d, aid: %d)", __func__, tid, aid);
return -1;
}
}
// Unpack clock values (see NVCtrl.h for details)
if (aid == ATTR_GPU_FREQ)
return value >> 16;
if (aid == ATTR_MEM_FREQ)
return value & 0xFFFF;
// Return value
return value;
}
// Retrieve attribute string via nvidia interface
static char* get_nvidia_string(TARGET_ID tid, ATTR_ID aid, int gid)
{
Display *dpy = nvdisplay ? nvdisplay : display;
char *str;
// Query nvidia interface
if (!dpy || !XNVCTRLQueryTargetStringAttribute(dpy, translate_nvidia_target[tid], gid, 0, translate_nvidia_attribute[aid], &str)) {
NORM_ERR("%s: Something went wrong running nvidia string query (tid: %d, aid: %d, GPU %d)", __func__, tid, aid, gid);
return NULL;
}
//fprintf(stderr, "checking get_nvidia_string-> '%s'", str);
return str;
}
static int cache_nvidia_string_value(TARGET_ID tid, ATTR_ID aid, char *token, SEARCH_ID search, int *value, int update, int gid)
{
static nvidia_c_string ac_string[MAXNUMGPU];
if (update) {
if (strcmp(token, (char*) "nvclockmin") == 0 && ac_string[gid].nvclockmin < 0){
ac_string[gid].nvclockmin = *value;
} else if (strcmp(token, (char*) "nvclockmax") == 0 && ac_string[gid].nvclockmax < 0){
ac_string[gid].nvclockmax = *value;
} else if (strcmp(token, (char*) "memclockmin") == 0 && ac_string[gid].memclockmin < 0){
ac_string[gid].memclockmin = *value;
} else if (strcmp(token, (char*) "memclockmax") == 0 && ac_string[gid].memclockmax < 0){
ac_string[gid].memclockmax = *value;
} else if (strcmp(token, (char*) "memTransferRatemin") == 0 && ac_string[gid].memTransferRatemin < 0){
ac_string[gid].memTransferRatemin = *value;
} else if (strcmp(token, (char*) "memTransferRatemax") == 0 && ac_string[gid].memTransferRatemax < 0){
ac_string[gid].memTransferRatemax = *value;
} else if (strcmp(token, (char*) "perf") == 0 && ac_string[gid].memTransferRatemax < 0){
if (search == SEARCH_MIN) {
ac_string[gid].perfmin = *value;
} else if (search == SEARCH_MAX) {
ac_string[gid].perfmax = *value;
}
}
} else {
if (strcmp(token, (char*) "nvclockmin") == 0){
*value = ac_string[gid].nvclockmin;
} else if (strcmp(token, (char*) "nvclockmax") == 0){
*value = ac_string[gid].nvclockmax;
} else if (strcmp(token, (char*) "memclockmin") == 0){
*value = ac_string[gid].memclockmin;
} else if (strcmp(token, (char*) "memclockmax") == 0){
*value = ac_string[gid].memclockmax;
} else if (strcmp(token, (char*) "memTransferRatemin") == 0){
*value = ac_string[gid].memTransferRatemin;
} else if (strcmp(token, (char*) "memTransferRatemax") == 0){
*value = ac_string[gid].memTransferRatemax;
} else if (strcmp(token, (char*) "perf") == 0){
if (search == SEARCH_MIN) {
*value = ac_string[gid].perfmin;
} else if (search == SEARCH_MAX) {
*value = ac_string[gid].perfmax;
}
}
}
return 0;
}
// Retrieve token value from nvidia string
static int get_nvidia_string_value(TARGET_ID tid, ATTR_ID aid, char *token, SEARCH_ID search, int gid)
{
char *str;
char *kvp, *key, *val;
char *saveptr1, *saveptr2;
int value, temp;
value = -1;
// Checks if the value is cacheable and is already loaded
cache_nvidia_string_value(tid, aid, token, search, &value, 0, gid);
if ( value != -1 ) {
return value;
}
// Get string via nvidia interface
str = get_nvidia_string(tid, aid, gid);
// Split string into 'key=value' substrings, split substring
// into key and value, from value, check if token was found,
// convert value to int, evaluate value according to specified
// token search mode
kvp = strtok_r(str, NV_KVPAIR_SEPARATORS, &saveptr1);
while (kvp) {
key = strtok_r(kvp, NV_KEYVAL_SEPARATORS, &saveptr2);
val = strtok_r(NULL, NV_KEYVAL_SEPARATORS, &saveptr2);
if (key && val && (strcmp(token, key) == 0)) {
temp = (int) strtol(val, NULL, 0);
if (search == SEARCH_FIRST) {
value = temp;
break;
} else if (search == SEARCH_LAST) {
value = temp;
} else if (search == SEARCH_MIN) {
if ((value == -1) || (temp < value))
value = temp;
} else if (search == SEARCH_MAX) {
if (temp > value)
value = temp;
} else {
value = -1;
break;
}
}
kvp = strtok_r(NULL, NV_KVPAIR_SEPARATORS, &saveptr1);
}
// This call updated the cache for the cacheable values;
cache_nvidia_string_value(tid, aid, token, search, &value, 1, gid);
// TESTING - print raw string if token was not found;
// string has to be queried again due to strtok_r()
/*if (value == -1) {
free(str);
str = get_nvidia_string(tid, aid);
fprintf(stderr, "%s", str);
}*/
// Free string, return value
free(str);
return value;
}
// Perform query and print result
void print_nvidia_value(struct text_object *obj, char *p, int p_max_size)
{
struct nvidia_s *nvs = static_cast(obj->data.opaque);
int value, temp1, temp2;
char* str;
Display *dpy = nvdisplay ? nvdisplay : display;
//num_GPU calculated only once based on the physical target
static int num_GPU = get_nvidia_target_count(dpy, TARGET_GPU) - 1;
// Assume failure
value = -1;
str = NULL;
// Perform query
if (nvs != NULL) {
//Reduce overcommitted GPU number to last GPU
if( nvs->gpu_id > num_GPU ) nvs->gpu_id = num_GPU;
//Execute switch by query type
switch (nvs->query) {
case QUERY_VALUE:
value = get_nvidia_value(nvs->target, nvs->attribute, nvs->gpu_id);
break;
case QUERY_STRING:
str = get_nvidia_string(nvs->target, nvs->attribute, nvs->gpu_id);
break;
case QUERY_STRING_VALUE:
value = get_nvidia_string_value(nvs->target, nvs->attribute, nvs->token, nvs->search, nvs->gpu_id);
break;
case QUERY_SPECIAL:
switch (nvs->attribute) {
case ATTR_PERF_MODE:
temp1 = get_nvidia_value(nvs->target, nvs->attribute, nvs->gpu_id);
switch (temp1) {
case NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE:
temp2 = asprintf(&str, "Adaptive");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE:
temp2 = asprintf(&str, "Max. Perf.");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_AUTO:
temp2 = asprintf(&str, "Auto");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE:
temp2 = asprintf(&str, "Consistent");
break;
default:
temp2 = asprintf(&str, "Unknown (%d)", value);
}
break;
case ATTR_MEM_FREE:
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->gpu_id);
value = temp2 - temp1;
break;
case ATTR_MEM_UTIL:
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
}
break;
}
}
// Print result
if (value != -1) {
snprintf(p, p_max_size, "%d", value);
} else if (str != NULL) {
snprintf(p, p_max_size, "%s", str);
free(str);
} else {
snprintf(p, p_max_size, "N/A");
}
}
double get_nvidia_barval(struct text_object *obj) {
struct nvidia_s *nvs = static_cast(obj->data.opaque);
int temp1, temp2;
double value;
// Assume failure
value = 0;
// Convert query_result to a percentage using ((val-min)÷(max-min)×100)+0.5 if needed.
if (nvs != NULL) {
switch (nvs->attribute) {
case ATTR_UTILS_STRING: // one of the percentage utils (gpuutil, membwutil, videoutil and pcieutil)
value = get_nvidia_string_value(nvs->target, ATTR_UTILS_STRING, nvs->token, nvs->search, nvs->gpu_id);
break;
case ATTR_MEM_UTIL: // memutil
case ATTR_MEM_USED:
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_MEM_FREE: // memfree
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->gpu_id);
value = temp2 - temp1;
break;
case ATTR_FAN_SPEED: // fanspeed: Warn user we are using fanlevel
NORM_ERR("%s: invalid argument specified: '%s' (using 'fanlevel' instead).",
nvs->command, nvs->arg);
// No break, continue into fanlevel
case ATTR_FAN_LEVEL: // fanlevel
value = get_nvidia_value(nvs->target, ATTR_FAN_LEVEL, nvs->gpu_id);
break;
case ATTR_GPU_TEMP: // gputemp (calculate out of gputempthreshold)
temp1 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP_THRESHOLD, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_AMBIENT_TEMP: // ambienttemp (calculate out of gputempthreshold for consistency)
temp1 = get_nvidia_value(nvs->target, ATTR_AMBIENT_TEMP, nvs->gpu_id);
temp2 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP_THRESHOLD, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_GPU_FREQ: // gpufreq (calculate out of gpufreqmax)
temp1 = get_nvidia_value(nvs->target, ATTR_GPU_FREQ, nvs->gpu_id);
temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING, (char*) "nvclockmax", SEARCH_MAX, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_MEM_FREQ: // memfreq (calculate out of memfreqmax)
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_FREQ, nvs->gpu_id);
temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING, (char*) "memclockmax", SEARCH_MAX, nvs->gpu_id);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_FREQS_STRING: // mtrfreq (calculate out of memfreqmax)
if (nvs->token == "memTransferRate") {
// Just in case error for silly devs
CRIT_ERR(NULL, NULL, "%s: attribute is 'ATTR_FREQS_STRING' but token is not \"memTransferRate\" (arg: '%s')",
nvs->command, nvs->arg);
return 0;
}
temp1 = get_nvidia_string_value(nvs->target, ATTR_FREQS_STRING, nvs->token, SEARCH_MAX, nvs->gpu_id);
temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING, (char*) "memTransferRatemax", SEARCH_MAX, nvs->gpu_id);
if (temp2 > temp1) temp1 = temp2; // extra safe here
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
case ATTR_IMAGE_QUALITY: // imagequality
value = get_nvidia_value(nvs->target, ATTR_IMAGE_QUALITY, nvs->gpu_id);
break;
default: // Throw error if unsupported args are used
CRIT_ERR(NULL, NULL, "%s: invalid argument specified: '%s'",
nvs->command, nvs->arg);
}
}
// Return the percentage
return value;
}
// Cleanup
void free_nvidia(struct text_object *obj)
{
free_and_zero(obj->data.opaque);
}