Skip to content

Commit 68ddb34

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. == 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. == NVIDIA Jetson models == Tested for NVIDIA Jetson Orin and Xavier boards.
1 parent 7720dbd commit 68ddb34

File tree

10 files changed

+306
-9
lines changed

10 files changed

+306
-9
lines changed

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,

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

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
@@ -119,6 +119,13 @@ char* xStrndup(const char* str, size_t len);
119119

120120
ATTR_NONNULL ATTR_ACCESS3_W(2, 3)
121121
ssize_t xReadfile(const char* pathname, void* buffer, size_t count);
122+
#ifdef NVIDIA_JETSON /* uncomment if you need this functionality somewhere else */
123+
ATTR_NONNULL ATTR_ACCESS3_W(2, 3)
124+
static inline double xReadNumberFromFile(const char *pathname, char *buf, const size_t len) {
125+
ssize_t nread = xReadfile(pathname, buf, len);
126+
return nread > 0 ? strtod(buf, NULL) : NAN;
127+
}
128+
#endif
122129
ATTR_NONNULL ATTR_ACCESS3_W(3, 4)
123130
ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count);
124131

@@ -174,4 +181,9 @@ static inline int xDirfd(DIR* dirp) {
174181
return r;
175182
}
176183

184+
185+
static inline double ConvCelsiusToFahrenheit(const double celsius) {
186+
return celsius * 9 / 5 + 32;
187+
}
188+
177189
#endif

configure.ac

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,47 @@ case "$enable_sensors" in
10951095
AC_MSG_ERROR([bad value '$enable_sensors' for --enable-sensors])
10961096
;;
10971097
esac
1098-
if test "$enable_sensors" = yes || test "$my_htop_platform" = freebsd; then
1098+
1099+
AC_ARG_ENABLE([nvidia-jetson],
1100+
[AS_HELP_STRING([--enable-nvidia-jetson],
1101+
[enable nvidia jetson support @<:@default=check@:>@])],
1102+
[],
1103+
[enable_nvidia_jetson=check])
1104+
case "$enable_nvidia_jetson" in
1105+
no)
1106+
;;
1107+
check)
1108+
if test -f "/etc/nv_tegra_release"; then
1109+
if grep -q "BOARD" "/etc/nv_tegra_release"; then
1110+
enable_nvidia_jetson=yes
1111+
fi
1112+
fi
1113+
;;
1114+
yes)
1115+
if test -f "/etc/nv_tegra_release"; then
1116+
if grep -q "BOARD" "/etc/nv_tegra_release"; then
1117+
enable_nvidia_jetson=yes
1118+
else
1119+
enable_nvidia_jetson=no
1120+
fi
1121+
else
1122+
enable_nvidia_jetson=no
1123+
fi
1124+
;;
1125+
*)
1126+
AC_MSG_ERROR([bad value '$enable_nvidia_jetson' for --enable-nvidia-jetson])
1127+
;;
1128+
esac
1129+
1130+
if test "$enable_nvidia_jetson" = yes; then
1131+
AC_DEFINE([NVIDIA_JETSON], [1], [Detected correct NVIDIA Jetson board])
1132+
else
1133+
AC_MSG_NOTICE([This is not a NVIDIA Jetson board])
1134+
fi
1135+
1136+
AM_CONDITIONAL([NVIDIA_JETSON], [test "$enable_nvidia_jetson" = yes])
1137+
1138+
if test "$enable_sensors" = yes || test "$my_htop_platform" = freebsd || test "$enable_nvidia_jetson" = yes; then
10991139
AC_DEFINE([BUILD_WITH_CPU_TEMP], [1], [Define if CPU temperature option should be enabled.])
11001140
fi
11011141

@@ -1233,6 +1273,7 @@ AC_MSG_RESULT([
12331273
(Linux) delay accounting: $enable_delayacct
12341274
(Linux) sensors: $enable_sensors
12351275
(Linux) capabilities: $enable_capabilities
1276+
(Linux) nvidia-jeston $enable_nvidia_jetson
12361277
unicode: $enable_unicode
12371278
affinity: $enable_affinity
12381279
unwind: $enable_unwind

linux/LinuxMachine.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ in the source distribution for its full text.
3232
#include "UsersTable.h"
3333
#include "XUtils.h"
3434

35+
#include "linux/NvidiaJetson.h"
3536
#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
3637

3738
#ifdef HAVE_SENSORS_SENSORS_H
@@ -736,15 +737,20 @@ void Machine_scan(Machine* super) {
736737
const Settings* settings = super->settings;
737738
if (settings->showCPUFrequency
738739
#ifdef HAVE_SENSORS_SENSORS_H
739-
|| settings->showCPUTemperature
740+
|| settings->showCPUTemperature /* TODO: looks like this line in the condition might be removed */
740741
#endif
741742
)
742743
LinuxMachine_scanCPUFrequency(this);
743744

745+
#ifdef NVIDIA_JETSON
746+
if (settings->showCPUTemperature)
747+
NvidiaJetson_getCPUTemperatures(this->cpuData, super->existingCPUs);
748+
#else
744749
#ifdef HAVE_SENSORS_SENSORS_H
745750
if (settings->showCPUTemperature)
746751
LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs);
747752
#endif
753+
#endif
748754
}
749755

750756
Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
@@ -787,6 +793,10 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
787793
// Initialize CPU count
788794
LinuxMachine_updateCPUcount(this);
789795

796+
#ifdef NVIDIA_JETSON
797+
NvidiaJetson_FindSensors();
798+
#endif
799+
790800
#ifdef HAVE_SENSORS_SENSORS_H
791801
// Fetch CPU topology
792802
LinuxMachine_fetchCPUTopologyFromCPUinfo(this);

linux/LinuxMachine.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ typedef struct CPUData_ {
4646

4747
double frequency;
4848

49-
#ifdef HAVE_SENSORS_SENSORS_H
49+
#ifdef BUILD_WITH_CPU_TEMP
5050
double temperature;
51-
51+
#endif
52+
#ifdef HAVE_SENSORS_SENSORS_H
5253
int physicalID; /* different for each CPU socket */
5354
int coreID; /* same for hyperthreading */
5455
int ccdID; /* same for each AMD chiplet */

linux/NvidiaJetson.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#include "config.h"
2+
#ifdef NVIDIA_JETSON
3+
4+
5+
#include "CRT.h"
6+
#include "Meter.h"
7+
#include "XUtils.h"
8+
#include "linux/NvidiaJetson.h"
9+
10+
11+
#include <ctype.h>
12+
#include <dirent.h>
13+
14+
15+
/*
16+
17+
NVIDIA Jetson devices support is located here.
18+
19+
Jetson has:
20+
- one CPU temperature sensor per 8 cores.
21+
- one GPU temperature sensor, on Jetson Orin it goes off if no GPU load: user gets error on file open
22+
23+
The code tries to find out the correct sensors during the application startup. As an example, the sensors
24+
location for NVIDIA Jetson Orin:
25+
- CPU temperature: /sys/devices/virtual/thermal/thermal_zone0/type
26+
- GPU temperature: /sys/devices/virtual/thermal/thermal_zone1/type
27+
- GPU frequency: /sys/class/devfreq/17000000.gpu/cur_freq
28+
- GPU curr load: /sys/class/devfreq/17000000.gpu/device/load
29+
30+
Measure:
31+
- The GPU frequency is provided in Hz, shown in MHz.
32+
- The CPU/GPU temperatures are provided in Celsius multipled by 1000 (milli Celsius), shown in Cesius
33+
- The Farenheit support is not provided
34+
*/
35+
36+
37+
/* global paths per each sensor */
38+
char CPU_TEMP_SENSOR_PATH[64];
39+
char GPU_TEMP_SENSOR_PATH[64];
40+
char GPU_FREQ_SENSOR_PATH[64];
41+
char GPU_LOAD_SENSOR_PATH[64];
42+
43+
44+
static inline bool IsJetsonOrinGPU(const char *name) {
45+
return strstr(name, "gpu");
46+
}
47+
48+
49+
static inline bool IsJetsonXavierGPU(const char *name) {
50+
return strstr(name, "gv11");
51+
}
52+
53+
54+
static void NvidiaJetson_FindGPUDevice(void) {
55+
const struct dirent* entry;
56+
57+
DIR* dir = opendir("/sys/class/devfreq");
58+
if (!dir)
59+
return;
60+
61+
while ((entry = readdir(dir)) != NULL) {
62+
const char* name = entry->d_name;
63+
64+
if (String_eq(name, ".") || String_eq(name, ".."))
65+
continue;
66+
67+
if (IsJetsonOrinGPU(name) || IsJetsonXavierGPU(name)) {
68+
xSnprintf(GPU_FREQ_SENSOR_PATH, sizeof(GPU_FREQ_SENSOR_PATH), "/sys/class/devfreq/%s/cur_freq", name);
69+
xSnprintf(GPU_LOAD_SENSOR_PATH, sizeof(GPU_LOAD_SENSOR_PATH), "/sys/class/devfreq/%s/device/load", name);
70+
break;
71+
}
72+
}
73+
closedir(dir);
74+
}
75+
76+
77+
static void NvidiaJetson_GoThroughThermalZones(void) {
78+
char path[64], content[4];
79+
const struct dirent* entry;
80+
81+
DIR* dir = opendir("/sys/devices/virtual/thermal");
82+
if (!dir)
83+
return;
84+
85+
while ((entry = readdir(dir)) != NULL && (CPU_TEMP_SENSOR_PATH[0] == 0 || GPU_TEMP_SENSOR_PATH[0] == 0)) {
86+
const char* name = entry->d_name;
87+
ssize_t ret;
88+
89+
if (String_eq(name, ".") || String_eq(name, ".."))
90+
continue;
91+
92+
if (!String_startsWith(name, "thermal_zone"))
93+
continue;
94+
95+
xSnprintf(path, sizeof(path), "/sys/devices/virtual/thermal/%s/type", name);
96+
ret = xReadfile(path, content, sizeof(content));
97+
if (ret <= 0)
98+
continue;
99+
100+
content[0] = tolower(content[0]);
101+
content[1] = tolower(content[1]);
102+
content[2] = tolower(content[2]);
103+
content[3] = tolower(content[3]);
104+
105+
if (CPU_TEMP_SENSOR_PATH[0] == 0 && String_startsWith(content, "cpu")) {
106+
xSnprintf(CPU_TEMP_SENSOR_PATH, sizeof(CPU_TEMP_SENSOR_PATH), "/sys/devices/virtual/thermal/%s/temp", name);
107+
}
108+
if (GPU_TEMP_SENSOR_PATH[0] == 0 && String_startsWith(content, "gpu")) {
109+
xSnprintf(GPU_TEMP_SENSOR_PATH, sizeof(GPU_TEMP_SENSOR_PATH), "/sys/devices/virtual/thermal/%s/temp", name);
110+
}
111+
}
112+
closedir(dir);
113+
}
114+
115+
116+
void NvidiaJetson_FindSensors(void) {
117+
NvidiaJetson_GoThroughThermalZones();
118+
NvidiaJetson_FindGPUDevice();
119+
}
120+
121+
122+
void NvidiaJetson_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs) {
123+
char buffer[22];
124+
double temp = xReadNumberFromFile(CPU_TEMP_SENSOR_PATH, buffer, sizeof(buffer)) / 1000.0;
125+
for (unsigned int i=0; i<=existingCPUs; ++i)
126+
cpus[i].temperature = temp;
127+
}
128+
129+
130+
enum JetsonValues {
131+
JETSON_GPU_LOAD = 0,
132+
JETSON_GPU_TEMP = 1,
133+
JETSON_GPU_FREQ = 2,
134+
JETSON_GPU_TOTAL_COUNT,
135+
};
136+
137+
138+
static void JetsonGPUMeter_updateValues(Meter* this) {
139+
char buffer[22];
140+
this->values[JETSON_GPU_LOAD] = xReadNumberFromFile(GPU_LOAD_SENSOR_PATH, buffer, sizeof(buffer));
141+
this->curItems = 1; /* only show bar for JETSON_GPU_LOAD */
142+
143+
this->values[JETSON_GPU_TEMP] = xReadNumberFromFile(GPU_TEMP_SENSOR_PATH, buffer, sizeof(buffer)) / 1000.0;
144+
this->values[JETSON_GPU_FREQ] = xReadNumberFromFile(GPU_FREQ_SENSOR_PATH, buffer, sizeof(buffer)) / 1000000.0;
145+
double percent = this->values[0] / 10.0;
146+
147+
char c = 'C';
148+
double gpuTemperature = this->values[JETSON_GPU_TEMP];
149+
if (this->host->settings->degreeFahrenheit) {
150+
gpuTemperature = ConvCelsiusToFahrenheit(gpuTemperature);
151+
c = 'F';
152+
}
153+
154+
unsigned int gpuFrequency = this->values[JETSON_GPU_FREQ];
155+
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%% %3uMHz %.1f%s%c",
156+
percent, gpuFrequency, gpuTemperature, CRT_degreeSign, c
157+
);
158+
}
159+
160+
static void JetsonGPUMeter_display(const Object* cast, RichString* out) {
161+
char buffer[32];
162+
const Meter* this = (const Meter*)cast;
163+
164+
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
165+
xSnprintf(buffer, sizeof(buffer), "%.1f", this->values[JETSON_GPU_LOAD]);
166+
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
167+
168+
RichString_appendAscii(out, CRT_colors[METER_TEXT], " freq:");
169+
xSnprintf(buffer, sizeof(buffer), "%3uMHz", (unsigned)this->values[JETSON_GPU_FREQ]);
170+
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
171+
172+
RichString_appendAscii(out, CRT_colors[METER_TEXT], " temp:");
173+
xSnprintf(buffer, sizeof(buffer), "%.1f%sC", this->values[JETSON_GPU_TEMP], CRT_degreeSign);
174+
RichString_appendWide(out, CRT_colors[METER_VALUE], buffer);
175+
}
176+
177+
static const int JetsonGPUMeter_attributes[] = {
178+
DEFAULT_COLOR
179+
};
180+
181+
const MeterClass JetsonGPUMeter_class = {
182+
.super = {
183+
.extends = Class(Meter),
184+
.delete = Meter_delete,
185+
.display = JetsonGPUMeter_display,
186+
},
187+
.updateValues = JetsonGPUMeter_updateValues,
188+
.defaultMode = BAR_METERMODE,
189+
.supportedModes = METERMODE_DEFAULT_SUPPORTED,
190+
.maxItems = JETSON_GPU_TOTAL_COUNT,
191+
.total = 1000.0,
192+
.attributes = JetsonGPUMeter_attributes,
193+
.name = "jetson_gpu",
194+
.uiName = "Jetson GPU",
195+
.caption = "GPU"
196+
};
197+
#endif

0 commit comments

Comments
 (0)