From 0365cf4b20f85c132d5be2104a7180c85b037358 Mon Sep 17 00:00:00 2001 From: Jing Zhang Date: Wed, 3 Jul 2024 20:13:56 -0400 Subject: [PATCH] KEP-4540: Add CPUManager policy option strict-cpu-reservation Signed-off-by: Jing Zhang --- pkg/kubelet/cm/cpumanager/policy_options.go | 11 +++++++- pkg/kubelet/cm/cpumanager/policy_static.go | 29 ++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/pkg/kubelet/cm/cpumanager/policy_options.go b/pkg/kubelet/cm/cpumanager/policy_options.go index ac6d15ed0d3..43126b18ea9 100644 --- a/pkg/kubelet/cm/cpumanager/policy_options.go +++ b/pkg/kubelet/cm/cpumanager/policy_options.go @@ -33,6 +33,7 @@ const ( DistributeCPUsAcrossNUMAOption string = "distribute-cpus-across-numa" AlignBySocketOption string = "align-by-socket" DistributeCPUsAcrossCoresOption string = "distribute-cpus-across-cores" + StrictCPUReservationOption string = "strict-cpu-reservation" ) var ( @@ -40,6 +41,7 @@ var ( DistributeCPUsAcrossNUMAOption, AlignBySocketOption, DistributeCPUsAcrossCoresOption, + StrictCPUReservationOption, ) betaOptions = sets.New[string]( FullPCPUsOnlyOption, @@ -86,6 +88,8 @@ type StaticPolicyOptions struct { // cpus (HT) on different physical core. // This is a preferred policy so do not throw error if they have to packed in one physical core. DistributeCPUsAcrossCores bool + // Flag to remove reserved cores from the list of available cores + StrictCPUReservation bool } // NewStaticPolicyOptions creates a StaticPolicyOptions struct from the user configuration. @@ -121,7 +125,12 @@ func NewStaticPolicyOptions(policyOptions map[string]string) (StaticPolicyOption return opts, fmt.Errorf("bad value for option %q: %w", name, err) } opts.DistributeCPUsAcrossCores = optValue - + case StrictCPUReservationOption: + optValue, err := strconv.ParseBool(value) + if err != nil { + return opts, fmt.Errorf("bad value for option %q: %w", name, err) + } + opts.StrictCPUReservation = optValue default: // this should never be reached, we already detect unknown options, // but we keep it as further safety. diff --git a/pkg/kubelet/cm/cpumanager/policy_static.go b/pkg/kubelet/cm/cpumanager/policy_static.go index 19c98af703e..c16bfe5dc19 100644 --- a/pkg/kubelet/cm/cpumanager/policy_static.go +++ b/pkg/kubelet/cm/cpumanager/policy_static.go @@ -195,14 +195,19 @@ func (p *staticPolicy) validateState(s state.State) error { tmpAssignments := s.GetCPUAssignments() tmpDefaultCPUset := s.GetDefaultCPUSet() + allCPUs := p.topology.CPUDetails.CPUs() + if p.options.StrictCPUReservation { + allCPUs = allCPUs.Difference(p.reservedCPUs) + } + // Default cpuset cannot be empty when assignments exist if tmpDefaultCPUset.IsEmpty() { if len(tmpAssignments) != 0 { return fmt.Errorf("default cpuset cannot be empty") } // state is empty initialize - allCPUs := p.topology.CPUDetails.CPUs() s.SetDefaultCPUSet(allCPUs) + klog.InfoS("Static policy initialized", "defaultCPUSet", allCPUs) return nil } @@ -210,9 +215,16 @@ func (p *staticPolicy) validateState(s state.State) error { // 1. Check if the reserved cpuset is not part of default cpuset because: // - kube/system reserved have changed (increased) - may lead to some containers not being able to start // - user tampered with file - if !p.reservedCPUs.Intersection(tmpDefaultCPUset).Equals(p.reservedCPUs) { - return fmt.Errorf("not all reserved cpus: \"%s\" are present in defaultCpuSet: \"%s\"", - p.reservedCPUs.String(), tmpDefaultCPUset.String()) + if p.options.StrictCPUReservation { + if !p.reservedCPUs.Intersection(tmpDefaultCPUset).IsEmpty() { + return fmt.Errorf("some of strictly reserved cpus: \"%s\" are present in defaultCpuSet: \"%s\"", + p.reservedCPUs.Intersection(tmpDefaultCPUset).String(), tmpDefaultCPUset.String()) + } + } else { + if !p.reservedCPUs.Intersection(tmpDefaultCPUset).Equals(p.reservedCPUs) { + return fmt.Errorf("not all reserved cpus: \"%s\" are present in defaultCpuSet: \"%s\"", + p.reservedCPUs.String(), tmpDefaultCPUset.String()) + } } // 2. Check if state for static policy is consistent @@ -235,15 +247,20 @@ func (p *staticPolicy) validateState(s state.State) error { // the set of CPUs stored in the state. totalKnownCPUs := tmpDefaultCPUset.Clone() tmpCPUSets := []cpuset.CPUSet{} + tmpAllCPUs := p.topology.CPUDetails.CPUs() for pod := range tmpAssignments { for _, cset := range tmpAssignments[pod] { tmpCPUSets = append(tmpCPUSets, cset) } } totalKnownCPUs = totalKnownCPUs.Union(tmpCPUSets...) - if !totalKnownCPUs.Equals(p.topology.CPUDetails.CPUs()) { + if p.options.StrictCPUReservation { + tmpAllCPUs = tmpAllCPUs.Difference(p.reservedCPUs) + } + if !totalKnownCPUs.Equals(tmpAllCPUs) { return fmt.Errorf("current set of available CPUs \"%s\" doesn't match with CPUs in state \"%s\"", - p.topology.CPUDetails.CPUs().String(), totalKnownCPUs.String()) + tmpAllCPUs.String(), totalKnownCPUs.String()) + } return nil