Skip to content

Commit 035de36

Browse files
authored
[LEP-3454] fix(nvidia/temperature): skip negative temp margin (#1208)
Fix > margin threshold exceeded: GPU-188fa3d6-f7b4-bd1d-bbbd-18ca52447f14 has only 0 °C margin left to slowdown (threshold 0 °C) > margin threshold exceeded: GPU-188fa3d6-f7b4-bd1d-bbbd-18ca52447f14 has only -5 °C margin left to slowdown (threshold 0 °C) Signed-off-by: Gyuho Lee <gyuhol@nvidia.com>
1 parent e818ff3 commit 035de36

2 files changed

Lines changed: 146 additions & 1 deletion

File tree

components/accelerator/nvidia/temperature/component.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,14 @@ func (c *component) Check() components.CheckResult {
196196
// threshold as defined by NVML. NVML does not specify GPU core vs HBM; it is
197197
// whichever slowdown threshold is nearest (driver-defined).
198198
// ref. https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g42db93dc04fc99d253eadc2037a5232d
199-
if temp.ThresholdCelsiusSlowdown > 0 && temp.MarginTemperatureSupported {
199+
//
200+
// NOTE: Some older GPUs or H100 may return ThresholdCelsiusSlowdownMargin of 0 or negative values,
201+
// indicating the margin temperature feature is not reliably supported.
202+
// We skip the margin check in such cases to avoid false positive alerts.
203+
if temp.ThresholdCelsiusSlowdown > 0 && temp.MarginTemperatureSupported &&
204+
marginThreshold.CelsiusSlowdownMargin > 0 && // threshold of 0 means the margin check is disabled
205+
temp.ThresholdCelsiusSlowdownMargin > 0 { // margin <= 0 indicates unreliable/unsupported data
206+
200207
// margin left less than the threshold, indicating the GPU is approaching the slowdown threshold
201208
// e.g.,
202209
// 5°C margin left means the GPU is approaching the slowdown threshold at 82°C

components/accelerator/nvidia/temperature/component_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,144 @@ func TestCheckResult_HealthStates_NoExtraInfo(t *testing.T) {
12881288
assert.Empty(t, states[0].ExtraInfo)
12891289
}
12901290

1291+
// TestCheck_MarginTemperatureThresholdEdgeCases tests edge cases for margin temperature threshold:
1292+
// - When threshold is 0 (disabled), no alert should be triggered
1293+
// - When GPU reports margin of 0 or negative (unreliable data from old GPUs or H100), no alert should be triggered
1294+
func TestCheck_MarginTemperatureThresholdEdgeCases(t *testing.T) {
1295+
tests := []struct {
1296+
name string
1297+
thresholdMargin int32 // marginThreshold.CelsiusSlowdownMargin
1298+
gpuMargin int32 // temp.ThresholdCelsiusSlowdownMargin
1299+
expectHealthy apiv1.HealthStateType
1300+
expectReasonContains string
1301+
}{
1302+
{
1303+
name: "Threshold is 0 (disabled) - no alert even with low margin",
1304+
thresholdMargin: 0,
1305+
gpuMargin: 5,
1306+
expectHealthy: apiv1.HealthStateTypeHealthy,
1307+
expectReasonContains: "no temperature issue found",
1308+
},
1309+
{
1310+
name: "Threshold is 0 (disabled) - no alert with 0 margin",
1311+
thresholdMargin: 0,
1312+
gpuMargin: 0,
1313+
expectHealthy: apiv1.HealthStateTypeHealthy,
1314+
expectReasonContains: "no temperature issue found",
1315+
},
1316+
{
1317+
name: "Threshold is 0 (disabled) - no alert with negative margin",
1318+
thresholdMargin: 0,
1319+
gpuMargin: -5,
1320+
expectHealthy: apiv1.HealthStateTypeHealthy,
1321+
expectReasonContains: "no temperature issue found",
1322+
},
1323+
{
1324+
name: "GPU reports margin 0 (old GPU/H100 unreliable) - no alert even with positive threshold",
1325+
thresholdMargin: 10,
1326+
gpuMargin: 0,
1327+
expectHealthy: apiv1.HealthStateTypeHealthy,
1328+
expectReasonContains: "no temperature issue found",
1329+
},
1330+
{
1331+
name: "GPU reports negative margin (unreliable data) - no alert even with positive threshold",
1332+
thresholdMargin: 10,
1333+
gpuMargin: -1,
1334+
expectHealthy: apiv1.HealthStateTypeHealthy,
1335+
expectReasonContains: "no temperature issue found",
1336+
},
1337+
{
1338+
name: "GPU reports negative margin -5 (unreliable data) - no alert even with positive threshold",
1339+
thresholdMargin: 10,
1340+
gpuMargin: -5,
1341+
expectHealthy: apiv1.HealthStateTypeHealthy,
1342+
expectReasonContains: "no temperature issue found",
1343+
},
1344+
{
1345+
name: "Valid margin above threshold - healthy",
1346+
thresholdMargin: 10,
1347+
gpuMargin: 15,
1348+
expectHealthy: apiv1.HealthStateTypeHealthy,
1349+
expectReasonContains: "no temperature issue found",
1350+
},
1351+
{
1352+
name: "Valid margin equal to threshold - degraded",
1353+
thresholdMargin: 10,
1354+
gpuMargin: 10,
1355+
expectHealthy: apiv1.HealthStateTypeDegraded,
1356+
expectReasonContains: "margin threshold exceeded",
1357+
},
1358+
{
1359+
name: "Valid margin below threshold - degraded",
1360+
thresholdMargin: 10,
1361+
gpuMargin: 5,
1362+
expectHealthy: apiv1.HealthStateTypeDegraded,
1363+
expectReasonContains: "margin threshold exceeded",
1364+
},
1365+
}
1366+
1367+
for _, tt := range tests {
1368+
t.Run(tt.name, func(t *testing.T) {
1369+
// Save and restore default thresholds
1370+
original := GetDefaultThresholds()
1371+
defer SetDefaultMarginThreshold(original)
1372+
1373+
SetDefaultMarginThreshold(Thresholds{CelsiusSlowdownMargin: tt.thresholdMargin})
1374+
1375+
ctx := context.Background()
1376+
1377+
uuid := "gpu-uuid-123"
1378+
mockDeviceObj := &mock.Device{
1379+
GetUUIDFunc: func() (string, nvml.Return) {
1380+
return uuid, nvml.SUCCESS
1381+
},
1382+
}
1383+
mockDev := testutil.NewMockDevice(mockDeviceObj, "test-arch", "test-brand", "test-cuda", "test-pci")
1384+
1385+
devs := map[string]device.Device{
1386+
uuid: mockDev,
1387+
}
1388+
1389+
mockNVML := &mockNVMLInstance{
1390+
devices: devs,
1391+
exists: true,
1392+
prodName: "Test GPU",
1393+
}
1394+
1395+
temperature := Temperature{
1396+
UUID: uuid,
1397+
CurrentCelsiusGPUCore: 80,
1398+
CurrentCelsiusHBM: 80,
1399+
HBMTemperatureSupported: true,
1400+
ThresholdCelsiusSlowdownMargin: tt.gpuMargin,
1401+
MarginTemperatureSupported: true,
1402+
ThresholdCelsiusShutdown: 120,
1403+
ThresholdCelsiusSlowdown: 95,
1404+
ThresholdCelsiusMemMax: 100,
1405+
ThresholdCelsiusGPUMax: 100,
1406+
UsedPercentShutdown: "66.67",
1407+
UsedPercentSlowdown: "84.21",
1408+
UsedPercentMemMax: "80.00",
1409+
UsedPercentGPUMax: "80.00",
1410+
}
1411+
1412+
getTemperatureFunc := func(uuid string, dev device.Device) (Temperature, error) {
1413+
return temperature, nil
1414+
}
1415+
1416+
component := MockTemperatureComponent(ctx, mockNVML, getTemperatureFunc).(*component)
1417+
result := component.Check()
1418+
1419+
data, ok := result.(*checkResult)
1420+
require.True(t, ok, "result should be of type *checkResult")
1421+
1422+
require.NotNil(t, data, "data should not be nil")
1423+
assert.Equal(t, tt.expectHealthy, data.health, "health state mismatch for case: %s", tt.name)
1424+
assert.Contains(t, data.reason, tt.expectReasonContains, "reason should contain expected text for case: %s", tt.name)
1425+
})
1426+
}
1427+
}
1428+
12911429
func TestCheck_GPURequiresResetSuggestedActions(t *testing.T) {
12921430
ctx := context.Background()
12931431

0 commit comments

Comments
 (0)