Add the ability to sample GPU_POWER sensors. Because the sensors have a high latency we read them from a background thread which means we've added the requirement for pthreads. Signed-off-by: Tom St Denis <tom.stdenis at amd.com> (v2) Cleaned up CMake around pthreads (v3) Update readme, factor out some deletions, cleanup switch statement --- CMakeLists.txt | 4 +++ README | 5 ++- src/app/top.c | 90 ++++++++++++++++++++++++++++++++++++++++++++------ src/lib/CMakeLists.txt | 1 + src/lib/read_sensor.c | 37 +++++++++++++++++++++ src/umr.h | 5 +++ 6 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 src/lib/read_sensor.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ef78c97ad763..8d89445c39e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,9 @@ add_definitions(-DUMR_BUILD_REV=\"${GIT_REV}\") # Add local repository for FindXXX.cmake modules. SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules/" ${CMAKE_MODULE_PATH}) +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +find_package(Threads REQUIRED) + find_package(Curses REQUIRED) include_directories(${CURSES_INCLUDE_DIRS}) @@ -31,6 +34,7 @@ include_directories(${LIBDRM_INCLUDE_DIR}) set(REQUIRED_EXTERNAL_LIBS ${CURSES_LIBRARIES} ${PCIACCESS_LIBRARIES} + Threads::Threads ) # Global setting: build everything position independent diff --git a/README b/README index 13cdac663d20..8b75f05a13b5 100644 --- a/README +++ b/README @@ -28,9 +28,8 @@ mailing list at: Building --------- -To build umr you will need pciaccess, ncurses, and libdrm headers and -libraries. Which are available in both Fedora and Ubuntu (as well as -other distributions). To build umr: +To build umr you will need pciaccess, ncurses, libdrm, and pthread +headers and libraries. To build umr: $ mkdir build && cd build/ && cmake ../ $ make diff --git a/src/app/top.c b/src/app/top.c index d9a474d78cf7..92ab8ab854af 100644 --- a/src/app/top.c +++ b/src/app/top.c @@ -54,6 +54,7 @@ enum sensor_maps { SENSOR_IDENTITY=0, // x = x SENSOR_D1000, // x = x/1000 SENSOR_D100, // x = x/100 + SENSOR_WATT, }; enum sensor_print { @@ -61,6 +62,7 @@ enum sensor_print { SENSOR_MHZ, SENSOR_PERCENT, SENSOR_TEMP, + SENSOR_POWER, }; enum drm_print { @@ -222,6 +224,10 @@ static struct umr_bitfield stat_vi_sensor_bits[] = { { "GFX_MCLK", AMDGPU_PP_SENSOR_GFX_MCLK, SENSOR_D100|(SENSOR_MHZ<<4), &umr_bitfield_default }, { "GPU_LOAD", AMDGPU_PP_SENSOR_GPU_LOAD, SENSOR_PERCENT<<4, &umr_bitfield_default }, { "GPU_TEMP", AMDGPU_PP_SENSOR_GPU_TEMP, SENSOR_D1000|(SENSOR_TEMP<<4), &umr_bitfield_default }, + { "VDDC", AMDGPU_PP_SENSOR_GPU_POWER, SENSOR_WATT|(SENSOR_POWER<<4), &umr_bitfield_default }, + { "VDDCI", AMDGPU_PP_SENSOR_GPU_POWER, SENSOR_WATT|(SENSOR_POWER<<4), &umr_bitfield_default }, + { "MAX_GPU", AMDGPU_PP_SENSOR_GPU_POWER, SENSOR_WATT|(SENSOR_POWER<<4), &umr_bitfield_default }, + { "AVG_GPU", AMDGPU_PP_SENSOR_GPU_POWER, SENSOR_WATT|(SENSOR_POWER<<4), &umr_bitfield_default }, { NULL, 0, 0, NULL }, }; @@ -251,6 +257,21 @@ static struct umr_bitfield stat_drm_bits[] = { static FILE *logfile = NULL; +static volatile int sensor_thread_quit = 0; +static uint32_t gpu_power_data[4]; +static void *vi_sensor_thread(void *data) +{ + struct umr_asic asic = *((struct umr_asic*)data); + int size = sizeof(gpu_power_data); + char fname[128]; + + snprintf(fname, sizeof(fname)-1, "/sys/kernel/debug/dri/%d/amdgpu_sensors", asic.instance); + asic.fd.sensors = open(fname, O_RDWR); + while (!sensor_thread_quit) + umr_read_sensor(&asic, AMDGPU_PP_SENSOR_GPU_POWER, gpu_power_data, &size); + close(asic.fd.sensors); + return NULL; +} static unsigned long last_fence_emitted, last_fence_signaled, fence_signal_count, fence_emit_count; static void analyze_fence_info(struct umr_asic *asic) @@ -423,6 +444,9 @@ static void print_sensors(struct umr_bitfield *bits, uint64_t *counts) case SENSOR_TEMP: printw("%5d C ", counts[i]); break; + case SENSOR_POWER: + printw("%3d.%02d W ", counts[i]/100, counts[i]%100); + break; }; if ((++print_j & (top_options.wide ? 3 : 1)) != 0) printw(" |"); @@ -491,8 +515,8 @@ static void parse_bits(struct umr_asic *asic, uint32_t addr, struct umr_bitfield static void parse_sensors(struct umr_asic *asic, uint32_t addr, struct umr_bitfield *bits, uint64_t *counts, uint32_t *mask, uint32_t *cmp, uint64_t addr_mask) { - int j; - int32_t value; + int j, size, x; + uint32_t value[16]; (void)addr; (void)mask; @@ -502,13 +526,41 @@ static void parse_sensors(struct umr_asic *asic, uint32_t addr, struct umr_bitfi if (asic->fd.sensors < 0) return; - for (j = 0; bits[j].regname; j++) { - lseek(asic->fd.sensors, bits[j].start * 4, SEEK_SET); - read(asic->fd.sensors, &value, 4); - switch (bits[j].stop & 0x0F) { - case SENSOR_IDENTITY: counts[j] = value; break; // identity - case SENSOR_D1000: counts[j] = value/1000; break; // divide by 1000 (e.g. KHz => MHz) - case SENSOR_D100: counts[j] = value/100; break; // divide by 100 (e.g. 10KHz => MHz) + for (j = 0; bits[j].regname; ) { + size = 4; + if (bits[j].start == AMDGPU_PP_SENSOR_GPU_POWER || !umr_read_sensor(asic, bits[j].start, &value[0], &size)) { + x = 0; + if (bits[j].start == AMDGPU_PP_SENSOR_GPU_POWER) { + size = 4 * sizeof(uint32_t); + memcpy(value, gpu_power_data, size); + } + + while (size) { + switch (bits[j].stop & 0x0F) { + case SENSOR_IDENTITY: + counts[j] = value[x]; + break; + case SENSOR_D1000: + counts[j] = value[x] / 1000; + break; + case SENSOR_D100: + counts[j] = value[x] / 100; + break; + case SENSOR_WATT: + counts[j] = ((value[x] >> 8) * 1000); + if ((value[x] & 0xFF) < 100) + counts[j] += (value[x] & 0xFF) * 10; + else + counts[j] += value[x]; + counts[j] /= 10; // convert to centiwatts since we don't need 3 digits of excess precision + break; + } + size -= 4; + ++j; + ++x; + } + } else { + ++j; } } } @@ -813,12 +865,13 @@ static void toggle_logger(void) void umr_top(struct umr_asic *asic) { - int i, j, k; + int i, j, k, use_thread; struct timespec req; uint32_t rep; time_t tt; uint64_t ts; char hostname[64] = { 0 }; + pthread_t sensor_thread; if (getenv("HOSTNAME")) strcpy(hostname, getenv("HOSTNAME")); @@ -839,6 +892,18 @@ void umr_top(struct umr_asic *asic) if (stat_counters[i].is_sensor == 0) grab_bits(stat_counters[i].name, asic, stat_counters[i].bits, &stat_counters[i].addr); + sensor_thread_quit = 0; + use_thread = 0; + + // start thread to grab sensor data for VI + if (asic->family == FAMILY_VI) { + if (pthread_create(&sensor_thread, NULL, vi_sensor_thread, asic)) { + fprintf(stderr, "[ERROR] Cannot create vi_sensor_thread\n"); + return; + } + use_thread = 1; + } + initscr(); start_color(); cbreak(); @@ -968,4 +1033,9 @@ void umr_top(struct umr_asic *asic) refresh(); } endwin(); + + if (use_thread) { + sensor_thread_quit = 1; + pthread_join(sensor_thread, NULL); + } } diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 46c75d61acb4..a5f0bda38689 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(umrcore STATIC find_reg.c mmio.c query_drm.c + read_sensor.c read_sgpr.c read_vram.c ring_decode.c diff --git a/src/lib/read_sensor.c b/src/lib/read_sensor.c new file mode 100644 index 000000000000..c4907f83208f --- /dev/null +++ b/src/lib/read_sensor.c @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Tom St Denis <tom.stdenis at amd.com> + * + */ +#include "umr.h" + +int umr_read_sensor(struct umr_asic *asic, int sensor, void *dst, int *size) +{ + int r; + lseek(asic->fd.sensors, sensor*4, SEEK_SET); + r = read(asic->fd.sensors, dst, *size); + if (r != *size) { + return -1; + } + *size = r; + return 0; +} diff --git a/src/umr.h b/src/umr.h index f2bce13d104b..34e8809eb415 100644 --- a/src/umr.h +++ b/src/umr.h @@ -31,6 +31,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <pciaccess.h> +#include <pthread.h> /* sourced from amd_powerplay.h from the kernel */ enum amd_pp_sensors { @@ -43,6 +44,9 @@ enum amd_pp_sensors { AMDGPU_PP_SENSOR_GPU_LOAD, AMDGPU_PP_SENSOR_GFX_MCLK, AMDGPU_PP_SENSOR_GPU_TEMP, + AMDGPU_PP_SENSOR_VCE_POWER, + AMDGPU_PP_SENSOR_UVD_POWER, + AMDGPU_PP_SENSOR_GPU_POWER, }; enum chipfamily { @@ -409,6 +413,7 @@ void umr_enumerate_devices(void); int umr_get_wave_status(struct umr_asic *asic, unsigned se, unsigned sh, unsigned cu, unsigned simd, unsigned wave, struct umr_wave_status *ws); int umr_get_wave_sq_info(struct umr_asic *asic, unsigned se, unsigned sh, unsigned cu, struct umr_wave_status *ws); int umr_read_sgprs(struct umr_asic *asic, struct umr_wave_status *ws, uint32_t *dst); +int umr_read_sensor(struct umr_asic *asic, int sensor, void *dst, int *size); /* mmio helpers */ uint32_t umr_find_reg(struct umr_asic *asic, char *regname); -- 2.11.0