Skip to content

Commit 109046f

Browse files
Preliminary support for NVIDIA Jetson boards
NVIDIA Jetson device is an insdustrial Linux based embedded aarch64 platfrom with powerful builtin GPU, which is used for AI tasks, mostly for CV purposes. The support is provided via --enable-nvidia-jetson switch in the configure script. All the source code related to the NVIDIA Jetson is placed in the linux/NvidiaJetson.{h,c} source files and hidden by 'NVIDIA_JETSON' C preprocessor define. So, for x86_64 platforms the source code stays unchanged. Additional functionality added by this commit: 1. Fix for the CPU temperature reading. The Jetson device is not supported by libsensors. The CPU has 8 cores with only one CPU temperature sensor for all of them located in the thermal zone file. libsensor might be compiled in or turned off. The additional care was taken to provide successfull build with/without libsensors. 2. The Jetson GPU Meter was added: current load, frequency and temperature. 3. The exact GPU memory allocated by each process is loaded from the nvgpu kernel driver via sysfs and merged to the LinuxProcess data (field LinuxProcess::gpu_mem). The field "GPU_MEM" visualizes this field. For root user only. 4. Additional filter for processes which use GPU right now via hot key 'g', the help is supplied. For root user only. == Technical details == The code tries to find out the correct sensors during the application startup. As an example, the sensors location for NVIDIA Jetson Orin are the following: - CPU temperature: /sys/devices/virtual/thermal/thermal_zone0/type - GPU temperature: /sys/devices/virtual/thermal/thermal_zone1/type - GPU frequency: /sys/class/devfreq/17000000.gpu/cur_freq - GPU curr load: /sys/class/devfreq/17000000.gpu/device/load Measure: - The GPU frequency is provided in Hz, shown in MHz. - The CPU/GPU temperatures are provided in Celsius multipled by 1000 (milli Celsius), shown in Cesius P.S. The GUI shows all temperatures for NVIDIA Jetson with additional precision comparing to the default x86_64 platform. If htop starts with root privileges (effective user id is 0), the experimental code activates. It reads the fixed sysfs file /sys/kernel/debug/nvmap/iovmm/clients with the following content, e.g.: ``` CLIENT PROCESS PID SIZE user gpu_burn 7979 23525644K user gnome_shell 8119 5800K user Xorg 2651 17876K total 23549320K ``` Unfortunately, the /sys/kernel/debug/* files are allowed to read only for the root user, that's why the restriction applies. The patch also adds a separate field 'GPU_MEM', which reads data from the added LinuxProcess::gpu_mem field. The field stores memory allocated for GPU in kilobytes. It is populated by the function NvidiaJetson_LoadGpuProcessTable (the implementation is located in NvidiaJetson.c), which is called at the end of the function Machine_scanTables. Additionally, the new Action is added: actionToggleGpuFilter, which is activated by 'g' hot key (the help is updated appropriately). The GpuFilter shows only the processes which currently utilize GPU (i.e. highly extended nvmap/iovmm/clients table). It is achieved by the filtering machinery associated with ProcessTable::pidMatchList. The code below constructs GPU_PID_MATCH_LIST hash table, then actionToggleGpuFilter either stores it to the ProcessTable::pidMatchList or restores old value of ProcessTable::pidMatchList. The separate LinuxProcess's PROCESS_FLAG_LINUX_GPU_JETSON (or something ...) flag isn't added for GPU_MEM, because currently the functionality of population LinuxProcess::gpu_mem is shared with the GPU consumers filter construction. So, even if GPU_MEM field is not activated, the filter showing GPU consumers should work. This kind of architecture is chosen intentially since it saves memory for the hash table GPU_PID_MATCH_LIST (which is now actually a set), and therefore increases performance. All other approaches convert GPU_PID_MATCH_LIST to a true key/value storage (key = pid, value = gpu memory allocated) with further merge code. == NVIDIA Jetson models == Tested for NVIDIA Jetson Orin and Xavier boards.
1 parent 45d9a1d commit 109046f

19 files changed

+500
-9
lines changed

Action.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,29 @@ static Htop_Reaction actionTogglePauseUpdate(State* st) {
646646
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
647647
}
648648

649+
#ifdef NVIDIA_JETSON
650+
#include "NvidiaJetson.h"
651+
#include "ProcessTable.h"
652+
653+
static Htop_Reaction actionToggleGpuFilter(State* st) {
654+
static Hashtable *stash = NULL;
655+
656+
Hashtable *GpuPidMatchList = NvidiaJetson_GetPidMatchList();
657+
if (GpuPidMatchList) {
658+
st->showGpuProcesses = !st->showGpuProcesses;
659+
660+
ProcessTable *pt = (ProcessTable *)st->host->activeTable;
661+
if (st->showGpuProcesses) {
662+
stash = pt->pidMatchList;
663+
pt->pidMatchList = GpuPidMatchList;
664+
} else {
665+
pt->pidMatchList = stash;
666+
}
667+
}
668+
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
669+
}
670+
#endif
671+
649672
static const struct {
650673
const char* key;
651674
bool roInactive;
@@ -658,6 +681,9 @@ static const struct {
658681
{ .key = " F3 /: ", .roInactive = false, .info = "incremental name search" },
659682
{ .key = " F4 \\: ", .roInactive = false, .info = "incremental name filtering" },
660683
{ .key = " F5 t: ", .roInactive = false, .info = "tree view" },
684+
#ifdef NVIDIA_JETSON
685+
{ .key = " g: ", .roInactive = false, .info = "show GPU processes (root only)" },
686+
#endif
661687
{ .key = " p: ", .roInactive = false, .info = "toggle program path" },
662688
{ .key = " m: ", .roInactive = false, .info = "toggle merged command" },
663689
{ .key = " Z: ", .roInactive = false, .info = "pause/resume process updates" },
@@ -933,6 +959,9 @@ void Action_setBindings(Htop_Action* keys) {
933959
keys['a'] = actionSetAffinity;
934960
keys['c'] = actionTagAllChildren;
935961
keys['e'] = actionShowEnvScreen;
962+
#ifdef NVIDIA_JETSON
963+
keys['g'] = actionToggleGpuFilter;
964+
#endif
936965
keys['h'] = actionHelp;
937966
keys['k'] = actionKill;
938967
keys['l'] = actionLsof;

Action.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ typedef struct State_ {
3939
bool pauseUpdate;
4040
bool hideSelection;
4141
bool hideMeters;
42+
#ifdef NVIDIA_JETSON
43+
bool showGpuProcesses;
44+
#endif
4245
} State;
4346

4447
static inline bool State_hideFunctionBar(const State* st) {

CPUMeter.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,30 @@ static void CPUMeter_updateValues(Meter* this) {
9898
}
9999
}
100100

101+
#ifdef NVIDIA_JETSON
102+
if (settings->showCPUTemperature) {
103+
char c = 'C';
104+
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
105+
if (settings->degreeFahrenheit) {
106+
c = 'F';
107+
cpuTemperature = ConvCelsiusToFahrenheit(cpuTemperature);
108+
}
109+
/* snprintf correctly represents double NAN numbers as 'nan' */
110+
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%.1f%s%c", cpuTemperature, CRT_degreeSign, c);
111+
}
112+
#else
101113
#ifdef BUILD_WITH_CPU_TEMP
102114
if (settings->showCPUTemperature) {
103115
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
104116
if (isNaN(cpuTemperature))
105117
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
106118
else if (settings->degreeFahrenheit)
107-
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign);
119+
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(ConvCelsiusToFahrenheit(cpuTemperature)), CRT_degreeSign);
108120
else
109121
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign);
110122
}
111123
#endif
124+
#endif
112125

113126
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%s%s%s%s",
114127
cpuUsageBuffer,

CRT.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
231231
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
232232
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
233233
[DYNAMIC_WHITE] = ColorPair(White, Black),
234+
#ifdef NVIDIA_JETSON
235+
[GPU_FILTER] = A_BOLD | ColorPair(Red, Cyan),
236+
#endif
234237
},
235238
[COLORSCHEME_MONOCHROME] = {
236239
[RESET_COLOR] = A_NORMAL,

CRT.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ typedef enum ColorElements_ {
158158
DYNAMIC_MAGENTA,
159159
DYNAMIC_YELLOW,
160160
DYNAMIC_WHITE,
161+
#ifdef NVIDIA_JETSON
162+
GPU_FILTER,
163+
#endif
161164
LAST_COLORELEMENT
162165
} ColorElements;
163166

DisplayOptionsPanel.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
173173
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency)));
174174
#ifdef BUILD_WITH_CPU_TEMP
175175
Panel_add(super, (Object*) CheckItem_newByRef(
176-
#if defined(HTOP_LINUX)
177-
"Also show CPU temperature (requires libsensors)",
178-
#elif defined(HTOP_FREEBSD)
176+
#if defined(HTOP_FREEBSD) || defined(NVIDIA_JETSON)
179177
"Also show CPU temperature",
178+
#elif defined(HTOP_LINUX)
179+
"Also show CPU temperature (requires libsensors)",
180180
#else
181181
#error Unknown temperature implementation!
182182
#endif

Machine.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Released under the GNU GPLv2+, see the COPYING file
66
in the source distribution for its full text.
77
*/
88

9+
#include "NvidiaJetson.h"
910
#include "config.h" // IWYU pragma: keep
1011

1112
#include "Machine.h"
@@ -128,4 +129,8 @@ void Machine_scanTables(Machine* this) {
128129
}
129130

130131
Row_setUidColumnWidth(this->maxUserId);
132+
133+
#ifdef NVIDIA_JETSON
134+
NvidiaJetson_LoadGpuProcessTable(this->activeTable->table);
135+
#endif
131136
}

MainPanel.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
195195
if (this->state->pauseUpdate) {
196196
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
197197
}
198+
#ifdef NVIDIA_JETSON
199+
if (this->state->showGpuProcesses) {
200+
FunctionBar_append("GPU", CRT_colors[GPU_FILTER]);
201+
}
202+
#endif
198203
}
199204

200205
static void MainPanel_printHeader(Panel* super) {

Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ linux_platform_sources = \
215215
zfs/ZfsArcMeter.c \
216216
zfs/ZfsCompressedArcMeter.c
217217

218+
if NVIDIA_JETSON
219+
linux_platform_headers += linux/NvidiaJetson.h
220+
linux_platform_sources += linux/NvidiaJetson.c
221+
endif
222+
218223
if HAVE_DELAYACCT
219224
linux_platform_headers += linux/LibNl.h
220225
linux_platform_sources += linux/LibNl.c

XUtils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ char* xStrndup(const char* str, size_t len);
123123

124124
ATTR_NONNULL ATTR_ACCESS3_W(2, 3)
125125
ssize_t xReadfile(const char* pathname, void* buffer, size_t count);
126+
#ifdef NVIDIA_JETSON /* uncomment if you need this functionality somewhere else */
127+
ATTR_NONNULL ATTR_ACCESS3_W(2, 3)
128+
static inline double xReadNumberFromFile(const char *pathname, char *buf, const size_t len) {
129+
ssize_t nread = xReadfile(pathname, buf, len);
130+
return nread > 0 ? strtod(buf, NULL) : NAN;
131+
}
132+
#endif
126133
ATTR_NONNULL ATTR_ACCESS3_W(3, 4)
127134
ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count);
128135

@@ -178,4 +185,9 @@ static inline int xDirfd(DIR* dirp) {
178185
return r;
179186
}
180187

188+
189+
static inline double ConvCelsiusToFahrenheit(const double celsius) {
190+
return celsius * 9 / 5 + 32;
191+
}
192+
181193
#endif

0 commit comments

Comments
 (0)