diff --git a/pkg/kubelet/cm/cpumanager/policy_static.go b/pkg/kubelet/cm/cpumanager/policy_static.go index f5d275d8ea8..ae63c2017ca 100644 --- a/pkg/kubelet/cm/cpumanager/policy_static.go +++ b/pkg/kubelet/cm/cpumanager/policy_static.go @@ -290,11 +290,26 @@ func (p *staticPolicy) Allocate(s state.State, pod *v1.Pod, container *v1.Contai return nil } +// getAssignedCPUsOfSiblings returns assigned cpus of given container's siblings(all containers other than the given container) in the given pod `podUID`. +func getAssignedCPUsOfSiblings(s state.State, podUID string, containerName string) cpuset.CPUSet { + assignments := s.GetCPUAssignments() + cset := cpuset.NewCPUSet() + for name, cpus := range assignments[podUID] { + if containerName == name { + continue + } + cset = cset.Union(cpus) + } + return cset +} + func (p *staticPolicy) RemoveContainer(s state.State, podUID string, containerName string) error { klog.InfoS("Static policy: RemoveContainer", "podUID", podUID, "containerName", containerName) + cpusInUse := getAssignedCPUsOfSiblings(s, podUID, containerName) if toRelease, ok := s.GetCPUSet(podUID, containerName); ok { s.Delete(podUID, containerName) // Mutate the shared pool, adding released cpus. + toRelease = toRelease.Difference(cpusInUse) s.SetDefaultCPUSet(s.GetDefaultCPUSet().Union(toRelease)) } return nil diff --git a/pkg/kubelet/cm/cpumanager/policy_static_test.go b/pkg/kubelet/cm/cpumanager/policy_static_test.go index d2b641fe3a0..4e3255fff01 100644 --- a/pkg/kubelet/cm/cpumanager/policy_static_test.go +++ b/pkg/kubelet/cm/cpumanager/policy_static_test.go @@ -558,6 +558,62 @@ func runStaticPolicyTestCase(t *testing.T, testCase staticPolicyTest) { } } +func TestStaticPolicyReuseCPUs(t *testing.T) { + testCases := []struct { + staticPolicyTest + expCSetAfterAlloc cpuset.CPUSet + expCSetAfterRemove cpuset.CPUSet + }{ + { + staticPolicyTest: staticPolicyTest{ + description: "SingleSocketHT, DeAllocOneInitContainer", + topo: topoSingleSocketHT, + pod: makeMultiContainerPod( + []struct{ request, limit string }{ + {"4000m", "4000m"}}, // 0, 1, 4, 5 + []struct{ request, limit string }{ + {"2000m", "2000m"}}), // 0, 4 + containerName: "initContainer-0", + stAssignments: state.ContainerCPUAssignments{}, + stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), + }, + expCSetAfterAlloc: cpuset.NewCPUSet(2, 3, 6, 7), + expCSetAfterRemove: cpuset.NewCPUSet(1, 2, 3, 5, 6, 7), + }, + } + + for _, testCase := range testCases { + policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.NewCPUSet(), topologymanager.NewFakeManager(), nil) + + st := &mockState{ + assignments: testCase.stAssignments, + defaultCPUSet: testCase.stDefaultCPUSet, + } + pod := testCase.pod + + // allocate + for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) { + policy.Allocate(st, pod, &container) + } + if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSetAfterAlloc) { + t.Errorf("StaticPolicy Allocate() error (%v). expected default cpuset %v but got %v", + testCase.description, testCase.expCSetAfterAlloc, st.defaultCPUSet) + } + + // remove + policy.RemoveContainer(st, string(pod.UID), testCase.containerName) + + if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSetAfterRemove) { + t.Errorf("StaticPolicy RemoveContainer() error (%v). expected default cpuset %v but got %v", + testCase.description, testCase.expCSetAfterRemove, st.defaultCPUSet) + } + if _, found := st.assignments[string(pod.UID)][testCase.containerName]; found { + t.Errorf("StaticPolicy RemoveContainer() error (%v). expected (pod %v, container %v) not be in assignments %v", + testCase.description, testCase.podUID, testCase.containerName, st.assignments) + } + } +} + func TestStaticPolicyRemove(t *testing.T) { testCases := []staticPolicyTest{ {