From 9ac0f74445ab4c060ede14429475ba57bca86d53 Mon Sep 17 00:00:00 2001 From: lin121291 <4jp33f9e@gmail.com> Date: Wed, 29 Apr 2026 18:22:25 +0800 Subject: [PATCH 1/2] fix(scheduler): guard against zero-value division in ComputeScore (#1780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ComputeScore and ComputeDefaultScore divide by device capacity fields (Count, Totalcore, Totalmem) without zero-checks. When any field is 0, float32 division produces NaN/Inf, which breaks sort.Sort's ordering requirement and causes an index-out-of-range panic — crashing the entire scheduler extender and halting all GPU scheduling cluster-wide. Return score 0 for devices/nodes with zero capacity so the scheduler can gracefully mark them as unschedulable instead of panicking. Signed-off-by: lin121291 <4jp33f9e@gmail.com> --- pkg/scheduler/policy/gpu_policy.go | 4 ++++ pkg/scheduler/policy/gpu_policy_test.go | 24 +++++++++++++++++++++ pkg/scheduler/policy/node_policy.go | 4 ++++ pkg/scheduler/policy/node_policy_test.go | 27 ++++++++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/pkg/scheduler/policy/gpu_policy.go b/pkg/scheduler/policy/gpu_policy.go index 272e199b42..aa82272069 100644 --- a/pkg/scheduler/policy/gpu_policy.go +++ b/pkg/scheduler/policy/gpu_policy.go @@ -57,6 +57,10 @@ func (l DeviceUsageList) Less(i, j int) bool { } func (ds *DeviceListsScore) ComputeScore(requests device.ContainerDeviceRequests) { + if ds.Device.Count == 0 || ds.Device.Totalcore == 0 || ds.Device.Totalmem == 0 { + ds.Score = 0 + return + } request, core, mem := int32(0), int32(0), int32(0) // Here we are required to use the same type device for _, container := range requests { diff --git a/pkg/scheduler/policy/gpu_policy_test.go b/pkg/scheduler/policy/gpu_policy_test.go index c17b4fd261..012d281b0f 100644 --- a/pkg/scheduler/policy/gpu_policy_test.go +++ b/pkg/scheduler/policy/gpu_policy_test.go @@ -203,6 +203,30 @@ func TestComputeScore(t *testing.T) { requests device.ContainerDeviceRequests expectedScore float32 }{ + { + name: "Zero capacity device returns score 0 without panic", + device: &device.DeviceUsage{ + ID: "test-device", + Type: "type1", + Count: 0, + Totalcore: 0, + Totalmem: 0, + }, + requests: make(device.ContainerDeviceRequests), + expectedScore: 0, + }, + { + name: "Partial zero capacity (Count=0) returns score 0 without panic", + device: &device.DeviceUsage{ + ID: "test-device", + Type: "type1", + Count: 0, + Totalcore: 8, + Totalmem: 4096, + }, + requests: make(device.ContainerDeviceRequests), + expectedScore: 0, + }, { name: "ContainerDeviceRequests has no data", device: &device.DeviceUsage{ diff --git a/pkg/scheduler/policy/node_policy.go b/pkg/scheduler/policy/node_policy.go index a6b6392bce..b54fc3b3fb 100644 --- a/pkg/scheduler/policy/node_policy.go +++ b/pkg/scheduler/policy/node_policy.go @@ -87,6 +87,10 @@ func (ns *NodeScore) ComputeDefaultScore(devices DeviceUsageList) { totalCore += deviceLists.Device.Totalcore totalMem += deviceLists.Device.Totalmem } + if total == 0 || totalCore == 0 || totalMem == 0 { + ns.Score = 0 + return + } useScore := float32(used) / float32(total) coreScore := float32(usedCore) / float32(totalCore) memScore := float32(usedMem) / float32(totalMem) diff --git a/pkg/scheduler/policy/node_policy_test.go b/pkg/scheduler/policy/node_policy_test.go index b59d57aa8b..0dd3b29e45 100644 --- a/pkg/scheduler/policy/node_policy_test.go +++ b/pkg/scheduler/policy/node_policy_test.go @@ -347,6 +347,33 @@ func TestComputeDefaultScore(t *testing.T) { devices DeviceUsageList wantScore float32 }{ + { + name: "Zero capacity devices returns score 0 without panic", + nodeScore: NodeScore{ + NodeID: "node-zero", + Score: 0.0, + }, + devices: DeviceUsageList{ + DeviceLists: []*DeviceListsScore{ + {Device: &device.DeviceUsage{ + Count: 0, Totalcore: 0, Totalmem: 0, + Used: 0, Usedcores: 0, Usedmem: 0, + }, Score: 0}, + }, + }, + wantScore: 0, + }, + { + name: "Empty device list returns score 0 without panic", + nodeScore: NodeScore{ + NodeID: "node-empty", + Score: 0.0, + }, + devices: DeviceUsageList{ + DeviceLists: []*DeviceListsScore{}, + }, + wantScore: 0, + }, { name: "Test with no devices", nodeScore: NodeScore{ From e50c98a716e5c52491d3561083561b1d0ce6345d Mon Sep 17 00:00:00 2001 From: lin121291 <4jp33f9e@gmail.com> Date: Thu, 30 Apr 2026 16:10:39 +0800 Subject: [PATCH 2/2] fix(scheduler): add nil check for Device pointer in ComputeScore Signed-off-by: lin121291 <4jp33f9e@gmail.com> --- pkg/scheduler/policy/gpu_policy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/scheduler/policy/gpu_policy.go b/pkg/scheduler/policy/gpu_policy.go index aa82272069..7cf027738b 100644 --- a/pkg/scheduler/policy/gpu_policy.go +++ b/pkg/scheduler/policy/gpu_policy.go @@ -57,7 +57,7 @@ func (l DeviceUsageList) Less(i, j int) bool { } func (ds *DeviceListsScore) ComputeScore(requests device.ContainerDeviceRequests) { - if ds.Device.Count == 0 || ds.Device.Totalcore == 0 || ds.Device.Totalmem == 0 { + if ds.Device == nil || ds.Device.Count == 0 || ds.Device.Totalcore == 0 || ds.Device.Totalmem == 0 { ds.Score = 0 return }