mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
rebase: update K8s packages to v0.32.1
Update K8s packages in go.mod to v0.32.1 Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
9
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/OWNERS
generated
vendored
Normal file
9
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/OWNERS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- derekwaynecarr
|
||||
- klueska
|
||||
reviewers: []
|
||||
emeritus_approvers:
|
||||
- ConnorDoyle
|
||||
- lmdaly
|
222
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask/bitmask.go
generated
vendored
Normal file
222
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask/bitmask.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bitmask
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// BitMask interface allows hint providers to create BitMasks for TopologyHints
|
||||
type BitMask interface {
|
||||
Add(bits ...int) error
|
||||
Remove(bits ...int) error
|
||||
And(masks ...BitMask)
|
||||
Or(masks ...BitMask)
|
||||
Clear()
|
||||
Fill()
|
||||
IsEqual(mask BitMask) bool
|
||||
IsEmpty() bool
|
||||
IsSet(bit int) bool
|
||||
AnySet(bits []int) bool
|
||||
IsNarrowerThan(mask BitMask) bool
|
||||
IsLessThan(mask BitMask) bool
|
||||
IsGreaterThan(mask BitMask) bool
|
||||
String() string
|
||||
Count() int
|
||||
GetBits() []int
|
||||
}
|
||||
|
||||
type bitMask uint64
|
||||
|
||||
// NewEmptyBitMask creates a new, empty BitMask
|
||||
func NewEmptyBitMask() BitMask {
|
||||
s := bitMask(0)
|
||||
return &s
|
||||
}
|
||||
|
||||
// NewBitMask creates a new BitMask
|
||||
func NewBitMask(bits ...int) (BitMask, error) {
|
||||
s := bitMask(0)
|
||||
err := (&s).Add(bits...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Add adds the bits with topology affinity to the BitMask
|
||||
func (s *bitMask) Add(bits ...int) error {
|
||||
mask := *s
|
||||
for _, i := range bits {
|
||||
if i < 0 || i >= 64 {
|
||||
return fmt.Errorf("bit number must be in range 0-63")
|
||||
}
|
||||
mask |= 1 << uint64(i)
|
||||
}
|
||||
*s = mask
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes specified bits from BitMask
|
||||
func (s *bitMask) Remove(bits ...int) error {
|
||||
mask := *s
|
||||
for _, i := range bits {
|
||||
if i < 0 || i >= 64 {
|
||||
return fmt.Errorf("bit number must be in range 0-63")
|
||||
}
|
||||
mask &^= 1 << uint64(i)
|
||||
}
|
||||
*s = mask
|
||||
return nil
|
||||
}
|
||||
|
||||
// And performs and operation on all bits in masks
|
||||
func (s *bitMask) And(masks ...BitMask) {
|
||||
for _, m := range masks {
|
||||
*s &= *m.(*bitMask)
|
||||
}
|
||||
}
|
||||
|
||||
// Or performs or operation on all bits in masks
|
||||
func (s *bitMask) Or(masks ...BitMask) {
|
||||
for _, m := range masks {
|
||||
*s |= *m.(*bitMask)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear resets all bits in mask to zero
|
||||
func (s *bitMask) Clear() {
|
||||
*s = 0
|
||||
}
|
||||
|
||||
// Fill sets all bits in mask to one
|
||||
func (s *bitMask) Fill() {
|
||||
*s = bitMask(^uint64(0))
|
||||
}
|
||||
|
||||
// IsEmpty checks mask to see if all bits are zero
|
||||
func (s *bitMask) IsEmpty() bool {
|
||||
return *s == 0
|
||||
}
|
||||
|
||||
// IsSet checks bit in mask to see if bit is set to one
|
||||
func (s *bitMask) IsSet(bit int) bool {
|
||||
if bit < 0 || bit >= 64 {
|
||||
return false
|
||||
}
|
||||
return (*s & (1 << uint64(bit))) > 0
|
||||
}
|
||||
|
||||
// AnySet checks bit in mask to see if any provided bit is set to one
|
||||
func (s *bitMask) AnySet(bits []int) bool {
|
||||
for _, b := range bits {
|
||||
if s.IsSet(b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEqual checks if masks are equal
|
||||
func (s *bitMask) IsEqual(mask BitMask) bool {
|
||||
return *s == *mask.(*bitMask)
|
||||
}
|
||||
|
||||
// IsNarrowerThan checks if one mask is narrower than another.
|
||||
//
|
||||
// A mask is said to be "narrower" than another if it has lets bits set. If the
|
||||
// same number of bits are set in both masks, then the mask with more
|
||||
// lower-numbered bits set wins out.
|
||||
func (s *bitMask) IsNarrowerThan(mask BitMask) bool {
|
||||
if s.Count() == mask.Count() {
|
||||
return s.IsLessThan(mask)
|
||||
}
|
||||
return s.Count() < mask.Count()
|
||||
}
|
||||
|
||||
// IsLessThan checks which bitmask has more lower-numbered bits set.
|
||||
func (s *bitMask) IsLessThan(mask BitMask) bool {
|
||||
return *s < *mask.(*bitMask)
|
||||
}
|
||||
|
||||
// IsGreaterThan checks which bitmask has more higher-numbered bits set.
|
||||
func (s *bitMask) IsGreaterThan(mask BitMask) bool {
|
||||
return *s > *mask.(*bitMask)
|
||||
}
|
||||
|
||||
// String converts mask to string
|
||||
func (s *bitMask) String() string {
|
||||
grouping := 2
|
||||
for shift := 64 - grouping; shift > 0; shift -= grouping {
|
||||
if *s > (1 << uint(shift)) {
|
||||
return fmt.Sprintf("%0"+strconv.Itoa(shift+grouping)+"b", *s)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%0"+strconv.Itoa(grouping)+"b", *s)
|
||||
}
|
||||
|
||||
// Count counts number of bits in mask set to one
|
||||
func (s *bitMask) Count() int {
|
||||
return bits.OnesCount64(uint64(*s))
|
||||
}
|
||||
|
||||
// Getbits returns each bit number with bits set to one
|
||||
func (s *bitMask) GetBits() []int {
|
||||
var bits []int
|
||||
for i := uint64(0); i < 64; i++ {
|
||||
if (*s & (1 << i)) > 0 {
|
||||
bits = append(bits, int(i))
|
||||
}
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
// And is a package level implementation of 'and' between first and masks
|
||||
func And(first BitMask, masks ...BitMask) BitMask {
|
||||
s := *first.(*bitMask)
|
||||
s.And(masks...)
|
||||
return &s
|
||||
}
|
||||
|
||||
// Or is a package level implementation of 'or' between first and masks
|
||||
func Or(first BitMask, masks ...BitMask) BitMask {
|
||||
s := *first.(*bitMask)
|
||||
s.Or(masks...)
|
||||
return &s
|
||||
}
|
||||
|
||||
// IterateBitMasks iterates all possible masks from a list of bits,
|
||||
// issuing a callback on each mask.
|
||||
func IterateBitMasks(bits []int, callback func(BitMask)) {
|
||||
var iterate func(bits, accum []int, size int)
|
||||
iterate = func(bits, accum []int, size int) {
|
||||
if len(accum) == size {
|
||||
mask, _ := NewBitMask(accum...)
|
||||
callback(mask)
|
||||
return
|
||||
}
|
||||
for i := range bits {
|
||||
iterate(bits[i+1:], append(accum, bits[i]), size)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= len(bits); i++ {
|
||||
iterate(bits, []int{}, i)
|
||||
}
|
||||
}
|
83
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/fake_topology_manager.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/fake_topology_manager.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/admission"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
)
|
||||
|
||||
type fakeManager struct {
|
||||
hint *TopologyHint
|
||||
policy Policy
|
||||
}
|
||||
|
||||
// NewFakeManager returns an instance of FakeManager
|
||||
func NewFakeManager() Manager {
|
||||
klog.InfoS("NewFakeManager")
|
||||
return &fakeManager{}
|
||||
}
|
||||
|
||||
// NewFakeManagerWithHint returns an instance of fake topology manager with specified topology hints
|
||||
func NewFakeManagerWithHint(hint *TopologyHint) Manager {
|
||||
klog.InfoS("NewFakeManagerWithHint")
|
||||
return &fakeManager{
|
||||
hint: hint,
|
||||
policy: NewNonePolicy(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFakeManagerWithPolicy returns an instance of fake topology manager with specified policy
|
||||
func NewFakeManagerWithPolicy(policy Policy) Manager {
|
||||
klog.InfoS("NewFakeManagerWithPolicy")
|
||||
return &fakeManager{
|
||||
policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *fakeManager) GetAffinity(podUID string, containerName string) TopologyHint {
|
||||
klog.InfoS("GetAffinity", "podUID", podUID, "containerName", containerName)
|
||||
if m.hint == nil {
|
||||
return TopologyHint{}
|
||||
}
|
||||
|
||||
return *m.hint
|
||||
}
|
||||
|
||||
func (m *fakeManager) GetPolicy() Policy {
|
||||
return m.policy
|
||||
}
|
||||
|
||||
func (m *fakeManager) AddHintProvider(h HintProvider) {
|
||||
klog.InfoS("AddHintProvider", "hintProvider", h)
|
||||
}
|
||||
|
||||
func (m *fakeManager) AddContainer(pod *v1.Pod, container *v1.Container, containerID string) {
|
||||
klog.InfoS("AddContainer", "pod", klog.KObj(pod), "containerName", container.Name, "containerID", containerID)
|
||||
}
|
||||
|
||||
func (m *fakeManager) RemoveContainer(containerID string) error {
|
||||
klog.InfoS("RemoveContainer", "containerID", containerID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *fakeManager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
|
||||
klog.InfoS("Topology Admit Handler")
|
||||
return admission.GetPodAdmitResult(nil)
|
||||
}
|
109
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/numa_info.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/numa_info.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
|
||||
)
|
||||
|
||||
type NUMADistances map[int][]uint64
|
||||
|
||||
type NUMAInfo struct {
|
||||
Nodes []int
|
||||
NUMADistances NUMADistances
|
||||
}
|
||||
|
||||
func NewNUMAInfo(topology []cadvisorapi.Node, opts PolicyOptions) (*NUMAInfo, error) {
|
||||
var numaNodes []int
|
||||
distances := map[int][]uint64{}
|
||||
for _, node := range topology {
|
||||
numaNodes = append(numaNodes, node.Id)
|
||||
|
||||
var nodeDistance []uint64
|
||||
if opts.PreferClosestNUMA {
|
||||
nodeDistance = node.Distances
|
||||
if nodeDistance == nil {
|
||||
return nil, fmt.Errorf("error getting NUMA distances from cadvisor")
|
||||
}
|
||||
}
|
||||
distances[node.Id] = nodeDistance
|
||||
}
|
||||
|
||||
numaInfo := &NUMAInfo{
|
||||
Nodes: numaNodes,
|
||||
NUMADistances: distances,
|
||||
}
|
||||
|
||||
return numaInfo, nil
|
||||
}
|
||||
|
||||
func (n *NUMAInfo) Narrowest(m1 bitmask.BitMask, m2 bitmask.BitMask) bitmask.BitMask {
|
||||
if m1.IsNarrowerThan(m2) {
|
||||
return m1
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
func (n *NUMAInfo) Closest(m1 bitmask.BitMask, m2 bitmask.BitMask) bitmask.BitMask {
|
||||
// If the length of both bitmasks aren't the same, choose the one that is narrowest.
|
||||
if m1.Count() != m2.Count() {
|
||||
return n.Narrowest(m1, m2)
|
||||
}
|
||||
|
||||
m1Distance := n.NUMADistances.CalculateAverageFor(m1)
|
||||
m2Distance := n.NUMADistances.CalculateAverageFor(m2)
|
||||
// If average distance is the same, take bitmask with more lower-number bits set.
|
||||
if m1Distance == m2Distance {
|
||||
if m1.IsLessThan(m2) {
|
||||
return m1
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
// Otherwise, return the bitmask with the shortest average distance between NUMA nodes.
|
||||
if m1Distance < m2Distance {
|
||||
return m1
|
||||
}
|
||||
|
||||
return m2
|
||||
}
|
||||
|
||||
func (n NUMAInfo) DefaultAffinityMask() bitmask.BitMask {
|
||||
defaultAffinity, _ := bitmask.NewBitMask(n.Nodes...)
|
||||
return defaultAffinity
|
||||
}
|
||||
|
||||
func (d NUMADistances) CalculateAverageFor(bm bitmask.BitMask) float64 {
|
||||
// This should never happen, but just in case make sure we do not divide by zero.
|
||||
if bm.Count() == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var count float64 = 0
|
||||
var sum float64 = 0
|
||||
for _, node1 := range bm.GetBits() {
|
||||
for _, node2 := range bm.GetBits() {
|
||||
sum += float64(d[node1][node2])
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return sum / count
|
||||
}
|
361
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy.go
generated
vendored
Normal file
361
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy.go
generated
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
|
||||
)
|
||||
|
||||
// Policy interface for Topology Manager Pod Admit Result
|
||||
type Policy interface {
|
||||
// Returns Policy Name
|
||||
Name() string
|
||||
// Returns a merged TopologyHint based on input from hint providers
|
||||
// and a Pod Admit Handler Response based on hints and policy type
|
||||
Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool)
|
||||
}
|
||||
|
||||
// IsAlignmentGuaranteed return true if the given policy guarantees that either
|
||||
// the compute resources will be allocated within a NUMA boundary, or the allocation will fail at all.
|
||||
func IsAlignmentGuaranteed(p Policy) bool {
|
||||
// We are abusing the name, but atm this matches almost 1:1 the policy name
|
||||
// so we are not adding new fields for now.
|
||||
return p.Name() == PolicySingleNumaNode
|
||||
}
|
||||
|
||||
// Merge a TopologyHints permutation to a single hint by performing a bitwise-AND
|
||||
// of their affinity masks. The hint shall be preferred if all hits in the permutation
|
||||
// are preferred.
|
||||
func mergePermutation(defaultAffinity bitmask.BitMask, permutation []TopologyHint) TopologyHint {
|
||||
// Get the NUMANodeAffinity from each hint in the permutation and see if any
|
||||
// of them encode unpreferred allocations.
|
||||
preferred := true
|
||||
var numaAffinities []bitmask.BitMask
|
||||
for _, hint := range permutation {
|
||||
// Only consider hints that have an actual NUMANodeAffinity set.
|
||||
if hint.NUMANodeAffinity != nil {
|
||||
numaAffinities = append(numaAffinities, hint.NUMANodeAffinity)
|
||||
// Only mark preferred if all affinities are equal.
|
||||
if !hint.NUMANodeAffinity.IsEqual(numaAffinities[0]) {
|
||||
preferred = false
|
||||
}
|
||||
}
|
||||
// Only mark preferred if all affinities are preferred.
|
||||
if !hint.Preferred {
|
||||
preferred = false
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the affinities using a bitwise-and operation.
|
||||
mergedAffinity := bitmask.And(defaultAffinity, numaAffinities...)
|
||||
// Build a mergedHint from the merged affinity mask, setting preferred as
|
||||
// appropriate based on the logic above.
|
||||
return TopologyHint{mergedAffinity, preferred}
|
||||
}
|
||||
|
||||
func filterProvidersHints(providersHints []map[string][]TopologyHint) [][]TopologyHint {
|
||||
// Loop through all hint providers and save an accumulated list of the
|
||||
// hints returned by each hint provider. If no hints are provided, assume
|
||||
// that provider has no preference for topology-aware allocation.
|
||||
var allProviderHints [][]TopologyHint
|
||||
for _, hints := range providersHints {
|
||||
// If hints is nil, insert a single, preferred any-numa hint into allProviderHints.
|
||||
if len(hints) == 0 {
|
||||
klog.InfoS("Hint Provider has no preference for NUMA affinity with any resource")
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, accumulate the hints for each resource type into allProviderHints.
|
||||
for resource := range hints {
|
||||
if hints[resource] == nil {
|
||||
klog.InfoS("Hint Provider has no preference for NUMA affinity with resource", "resource", resource)
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(hints[resource]) == 0 {
|
||||
klog.InfoS("Hint Provider has no possible NUMA affinities for resource", "resource", resource)
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{nil, false}})
|
||||
continue
|
||||
}
|
||||
|
||||
allProviderHints = append(allProviderHints, hints[resource])
|
||||
}
|
||||
}
|
||||
return allProviderHints
|
||||
}
|
||||
|
||||
func narrowestHint(hints []TopologyHint) *TopologyHint {
|
||||
if len(hints) == 0 {
|
||||
return nil
|
||||
}
|
||||
var narrowestHint *TopologyHint
|
||||
for i := range hints {
|
||||
if hints[i].NUMANodeAffinity == nil {
|
||||
continue
|
||||
}
|
||||
if narrowestHint == nil {
|
||||
narrowestHint = &hints[i]
|
||||
}
|
||||
if hints[i].NUMANodeAffinity.IsNarrowerThan(narrowestHint.NUMANodeAffinity) {
|
||||
narrowestHint = &hints[i]
|
||||
}
|
||||
}
|
||||
return narrowestHint
|
||||
}
|
||||
|
||||
func maxOfMinAffinityCounts(filteredHints [][]TopologyHint) int {
|
||||
maxOfMinCount := 0
|
||||
for _, resourceHints := range filteredHints {
|
||||
narrowestHint := narrowestHint(resourceHints)
|
||||
if narrowestHint == nil {
|
||||
continue
|
||||
}
|
||||
if narrowestHint.NUMANodeAffinity.Count() > maxOfMinCount {
|
||||
maxOfMinCount = narrowestHint.NUMANodeAffinity.Count()
|
||||
}
|
||||
}
|
||||
return maxOfMinCount
|
||||
}
|
||||
|
||||
type HintMerger struct {
|
||||
NUMAInfo *NUMAInfo
|
||||
Hints [][]TopologyHint
|
||||
// Set bestNonPreferredAffinityCount to help decide which affinity mask is
|
||||
// preferred amongst all non-preferred hints. We calculate this value as
|
||||
// the maximum of the minimum affinity counts supplied for any given hint
|
||||
// provider. In other words, prefer a hint that has an affinity mask that
|
||||
// includes all of the NUMA nodes from the provider that requires the most
|
||||
// NUMA nodes to satisfy its allocation.
|
||||
BestNonPreferredAffinityCount int
|
||||
CompareNUMAAffinityMasks func(candidate *TopologyHint, current *TopologyHint) (best *TopologyHint)
|
||||
}
|
||||
|
||||
func NewHintMerger(numaInfo *NUMAInfo, hints [][]TopologyHint, policyName string, opts PolicyOptions) HintMerger {
|
||||
compareNumaAffinityMasks := func(current, candidate *TopologyHint) *TopologyHint {
|
||||
// If current and candidate bitmasks are the same, prefer current hint.
|
||||
if candidate.NUMANodeAffinity.IsEqual(current.NUMANodeAffinity) {
|
||||
return current
|
||||
}
|
||||
|
||||
// Otherwise compare the hints, based on the policy options provided
|
||||
var best bitmask.BitMask
|
||||
if (policyName != PolicySingleNumaNode) && opts.PreferClosestNUMA {
|
||||
best = numaInfo.Closest(current.NUMANodeAffinity, candidate.NUMANodeAffinity)
|
||||
} else {
|
||||
best = numaInfo.Narrowest(current.NUMANodeAffinity, candidate.NUMANodeAffinity)
|
||||
}
|
||||
if best.IsEqual(current.NUMANodeAffinity) {
|
||||
return current
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
merger := HintMerger{
|
||||
NUMAInfo: numaInfo,
|
||||
Hints: hints,
|
||||
BestNonPreferredAffinityCount: maxOfMinAffinityCounts(hints),
|
||||
CompareNUMAAffinityMasks: compareNumaAffinityMasks,
|
||||
}
|
||||
|
||||
return merger
|
||||
}
|
||||
|
||||
func (m HintMerger) compare(current *TopologyHint, candidate *TopologyHint) *TopologyHint {
|
||||
// Only consider candidates that result in a NUMANodeAffinity > 0 to
|
||||
// replace the current bestHint.
|
||||
if candidate.NUMANodeAffinity.Count() == 0 {
|
||||
return current
|
||||
}
|
||||
|
||||
// If no current bestHint is set, return the candidate as the bestHint.
|
||||
if current == nil {
|
||||
return candidate
|
||||
}
|
||||
|
||||
// If the current bestHint is non-preferred and the candidate hint is
|
||||
// preferred, always choose the preferred hint over the non-preferred one.
|
||||
if !current.Preferred && candidate.Preferred {
|
||||
return candidate
|
||||
}
|
||||
|
||||
// If the current bestHint is preferred and the candidate hint is
|
||||
// non-preferred, never update the bestHint, regardless of how
|
||||
// the candidate hint's affinity mask compares to the current
|
||||
// hint's affinity mask.
|
||||
if current.Preferred && !candidate.Preferred {
|
||||
return current
|
||||
}
|
||||
|
||||
// If the current bestHint and the candidate hint are both preferred,
|
||||
// then only consider fitter NUMANodeAffinity
|
||||
if current.Preferred && candidate.Preferred {
|
||||
return m.CompareNUMAAffinityMasks(current, candidate)
|
||||
|
||||
}
|
||||
|
||||
// The only case left is if the current best bestHint and the candidate
|
||||
// hint are both non-preferred. In this case, try and find a hint whose
|
||||
// affinity count is as close to (but not higher than) the
|
||||
// bestNonPreferredAffinityCount as possible. To do this we need to
|
||||
// consider the following cases and react accordingly:
|
||||
//
|
||||
// 1. current.NUMANodeAffinity.Count() > bestNonPreferredAffinityCount
|
||||
// 2. current.NUMANodeAffinity.Count() == bestNonPreferredAffinityCount
|
||||
// 3. current.NUMANodeAffinity.Count() < bestNonPreferredAffinityCount
|
||||
//
|
||||
// For case (1), the current bestHint is larger than the
|
||||
// bestNonPreferredAffinityCount, so updating to fitter mergeHint
|
||||
// is preferred over staying where we are.
|
||||
//
|
||||
// For case (2), the current bestHint is equal to the
|
||||
// bestNonPreferredAffinityCount, so we would like to stick with what
|
||||
// we have *unless* the candidate hint is also equal to
|
||||
// bestNonPreferredAffinityCount and it is fitter.
|
||||
//
|
||||
// For case (3), the current bestHint is less than
|
||||
// bestNonPreferredAffinityCount, so we would like to creep back up to
|
||||
// bestNonPreferredAffinityCount as close as we can. There are three
|
||||
// cases to consider here:
|
||||
//
|
||||
// 3a. candidate.NUMANodeAffinity.Count() > bestNonPreferredAffinityCount
|
||||
// 3b. candidate.NUMANodeAffinity.Count() == bestNonPreferredAffinityCount
|
||||
// 3c. candidate.NUMANodeAffinity.Count() < bestNonPreferredAffinityCount
|
||||
//
|
||||
// For case (3a), we just want to stick with the current bestHint
|
||||
// because choosing a new hint that is greater than
|
||||
// bestNonPreferredAffinityCount would be counter-productive.
|
||||
//
|
||||
// For case (3b), we want to immediately update bestHint to the
|
||||
// candidate hint, making it now equal to bestNonPreferredAffinityCount.
|
||||
//
|
||||
// For case (3c), we know that *both* the current bestHint and the
|
||||
// candidate hint are less than bestNonPreferredAffinityCount, so we
|
||||
// want to choose one that brings us back up as close to
|
||||
// bestNonPreferredAffinityCount as possible. There are three cases to
|
||||
// consider here:
|
||||
//
|
||||
// 3ca. candidate.NUMANodeAffinity.Count() > current.NUMANodeAffinity.Count()
|
||||
// 3cb. candidate.NUMANodeAffinity.Count() < current.NUMANodeAffinity.Count()
|
||||
// 3cc. candidate.NUMANodeAffinity.Count() == current.NUMANodeAffinity.Count()
|
||||
//
|
||||
// For case (3ca), we want to immediately update bestHint to the
|
||||
// candidate hint because that will bring us closer to the (higher)
|
||||
// value of bestNonPreferredAffinityCount.
|
||||
//
|
||||
// For case (3cb), we want to stick with the current bestHint because
|
||||
// choosing the candidate hint would strictly move us further away from
|
||||
// the bestNonPreferredAffinityCount.
|
||||
//
|
||||
// Finally, for case (3cc), we know that the current bestHint and the
|
||||
// candidate hint are equal, so we simply choose the fitter of the 2.
|
||||
|
||||
// Case 1
|
||||
if current.NUMANodeAffinity.Count() > m.BestNonPreferredAffinityCount {
|
||||
return m.CompareNUMAAffinityMasks(current, candidate)
|
||||
}
|
||||
// Case 2
|
||||
if current.NUMANodeAffinity.Count() == m.BestNonPreferredAffinityCount {
|
||||
if candidate.NUMANodeAffinity.Count() != m.BestNonPreferredAffinityCount {
|
||||
return current
|
||||
}
|
||||
return m.CompareNUMAAffinityMasks(current, candidate)
|
||||
}
|
||||
// Case 3a
|
||||
if candidate.NUMANodeAffinity.Count() > m.BestNonPreferredAffinityCount {
|
||||
return current
|
||||
}
|
||||
// Case 3b
|
||||
if candidate.NUMANodeAffinity.Count() == m.BestNonPreferredAffinityCount {
|
||||
return candidate
|
||||
}
|
||||
|
||||
// Case 3ca
|
||||
if candidate.NUMANodeAffinity.Count() > current.NUMANodeAffinity.Count() {
|
||||
return candidate
|
||||
}
|
||||
// Case 3cb
|
||||
if candidate.NUMANodeAffinity.Count() < current.NUMANodeAffinity.Count() {
|
||||
return current
|
||||
}
|
||||
|
||||
// Case 3cc
|
||||
return m.CompareNUMAAffinityMasks(current, candidate)
|
||||
|
||||
}
|
||||
|
||||
func (m HintMerger) Merge() TopologyHint {
|
||||
defaultAffinity := m.NUMAInfo.DefaultAffinityMask()
|
||||
|
||||
var bestHint *TopologyHint
|
||||
iterateAllProviderTopologyHints(m.Hints, func(permutation []TopologyHint) {
|
||||
// Get the NUMANodeAffinity from each hint in the permutation and see if any
|
||||
// of them encode unpreferred allocations.
|
||||
mergedHint := mergePermutation(defaultAffinity, permutation)
|
||||
|
||||
// Compare the current bestHint with the candidate mergedHint and
|
||||
// update bestHint if appropriate.
|
||||
bestHint = m.compare(bestHint, &mergedHint)
|
||||
})
|
||||
|
||||
if bestHint == nil {
|
||||
bestHint = &TopologyHint{defaultAffinity, false}
|
||||
}
|
||||
|
||||
return *bestHint
|
||||
}
|
||||
|
||||
// Iterate over all permutations of hints in 'allProviderHints [][]TopologyHint'.
|
||||
//
|
||||
// This procedure is implemented as a recursive function over the set of hints
|
||||
// in 'allproviderHints[i]'. It applies the function 'callback' to each
|
||||
// permutation as it is found. It is the equivalent of:
|
||||
//
|
||||
// for i := 0; i < len(providerHints[0]); i++
|
||||
//
|
||||
// for j := 0; j < len(providerHints[1]); j++
|
||||
// for k := 0; k < len(providerHints[2]); k++
|
||||
// ...
|
||||
// for z := 0; z < len(providerHints[-1]); z++
|
||||
// permutation := []TopologyHint{
|
||||
// providerHints[0][i],
|
||||
// providerHints[1][j],
|
||||
// providerHints[2][k],
|
||||
// ...
|
||||
// providerHints[-1][z]
|
||||
// }
|
||||
// callback(permutation)
|
||||
func iterateAllProviderTopologyHints(allProviderHints [][]TopologyHint, callback func([]TopologyHint)) {
|
||||
// Internal helper function to accumulate the permutation before calling the callback.
|
||||
var iterate func(i int, accum []TopologyHint)
|
||||
iterate = func(i int, accum []TopologyHint) {
|
||||
// Base case: we have looped through all providers and have a full permutation.
|
||||
if i == len(allProviderHints) {
|
||||
callback(accum)
|
||||
return
|
||||
}
|
||||
|
||||
// Loop through all hints for provider 'i', and recurse to build the
|
||||
// permutation of this hint with all hints from providers 'i++'.
|
||||
for j := range allProviderHints[i] {
|
||||
iterate(i+1, append(accum, allProviderHints[i][j]))
|
||||
}
|
||||
}
|
||||
iterate(0, []TopologyHint{})
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_best_effort.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_best_effort.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
type bestEffortPolicy struct {
|
||||
// numaInfo represents list of NUMA Nodes available on the underlying machine and distances between them
|
||||
numaInfo *NUMAInfo
|
||||
opts PolicyOptions
|
||||
}
|
||||
|
||||
var _ Policy = &bestEffortPolicy{}
|
||||
|
||||
// PolicyBestEffort policy name.
|
||||
const PolicyBestEffort string = "best-effort"
|
||||
|
||||
// NewBestEffortPolicy returns best-effort policy.
|
||||
func NewBestEffortPolicy(numaInfo *NUMAInfo, opts PolicyOptions) Policy {
|
||||
return &bestEffortPolicy{numaInfo: numaInfo, opts: opts}
|
||||
}
|
||||
|
||||
func (p *bestEffortPolicy) Name() string {
|
||||
return PolicyBestEffort
|
||||
}
|
||||
|
||||
func (p *bestEffortPolicy) canAdmitPodResult(hint *TopologyHint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *bestEffortPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
|
||||
filteredHints := filterProvidersHints(providersHints)
|
||||
merger := NewHintMerger(p.numaInfo, filteredHints, p.Name(), p.opts)
|
||||
bestHint := merger.Merge()
|
||||
admit := p.canAdmitPodResult(&bestHint)
|
||||
return bestHint, admit
|
||||
}
|
41
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_none.go
generated
vendored
Normal file
41
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_none.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
type nonePolicy struct{}
|
||||
|
||||
var _ Policy = &nonePolicy{}
|
||||
|
||||
// PolicyNone policy name.
|
||||
const PolicyNone string = "none"
|
||||
|
||||
// NewNonePolicy returns none policy.
|
||||
func NewNonePolicy() Policy {
|
||||
return &nonePolicy{}
|
||||
}
|
||||
|
||||
func (p *nonePolicy) Name() string {
|
||||
return PolicyNone
|
||||
}
|
||||
|
||||
func (p *nonePolicy) canAdmitPodResult(hint *TopologyHint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *nonePolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
|
||||
return TopologyHint{}, p.canAdmitPodResult(nil)
|
||||
}
|
105
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_options.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_options.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
const (
|
||||
PreferClosestNUMANodes string = "prefer-closest-numa-nodes"
|
||||
MaxAllowableNUMANodes string = "max-allowable-numa-nodes"
|
||||
)
|
||||
|
||||
var (
|
||||
alphaOptions = sets.New[string]()
|
||||
betaOptions = sets.New[string](
|
||||
MaxAllowableNUMANodes,
|
||||
)
|
||||
stableOptions = sets.New[string](
|
||||
PreferClosestNUMANodes,
|
||||
)
|
||||
)
|
||||
|
||||
func CheckPolicyOptionAvailable(option string) error {
|
||||
if !alphaOptions.Has(option) && !betaOptions.Has(option) && !stableOptions.Has(option) {
|
||||
return fmt.Errorf("unknown Topology Manager Policy option: %q", option)
|
||||
}
|
||||
|
||||
if alphaOptions.Has(option) && !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManagerPolicyAlphaOptions) {
|
||||
return fmt.Errorf("Topology Manager Policy Alpha-level Options not enabled, but option %q provided", option)
|
||||
}
|
||||
|
||||
if betaOptions.Has(option) && !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManagerPolicyBetaOptions) {
|
||||
return fmt.Errorf("Topology Manager Policy Beta-level Options not enabled, but option %q provided", option)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PolicyOptions struct {
|
||||
PreferClosestNUMA bool
|
||||
MaxAllowableNUMANodes int
|
||||
}
|
||||
|
||||
func NewPolicyOptions(policyOptions map[string]string) (PolicyOptions, error) {
|
||||
opts := PolicyOptions{
|
||||
// Set MaxAllowableNUMANodes to the default. This will be overwritten
|
||||
// if the user has specified a policy option for MaxAllowableNUMANodes.
|
||||
MaxAllowableNUMANodes: defaultMaxAllowableNUMANodes,
|
||||
}
|
||||
|
||||
for name, value := range policyOptions {
|
||||
if err := CheckPolicyOptionAvailable(name); err != nil {
|
||||
return opts, err
|
||||
}
|
||||
|
||||
switch name {
|
||||
case PreferClosestNUMANodes:
|
||||
optValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return opts, fmt.Errorf("bad value for option %q: %w", name, err)
|
||||
}
|
||||
opts.PreferClosestNUMA = optValue
|
||||
case MaxAllowableNUMANodes:
|
||||
optValue, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return opts, fmt.Errorf("unable to convert policy option to integer %q: %w", name, err)
|
||||
}
|
||||
|
||||
if optValue < defaultMaxAllowableNUMANodes {
|
||||
return opts, fmt.Errorf("the minimum value of %q should not be less than %v", name, defaultMaxAllowableNUMANodes)
|
||||
}
|
||||
|
||||
if optValue > defaultMaxAllowableNUMANodes {
|
||||
klog.InfoS("WARNING: the value of max-allowable-numa-nodes is more than the default recommended value", "max-allowable-numa-nodes", optValue, "defaultMaxAllowableNUMANodes", defaultMaxAllowableNUMANodes)
|
||||
}
|
||||
opts.MaxAllowableNUMANodes = optValue
|
||||
default:
|
||||
// this should never be reached, we already detect unknown options,
|
||||
// but we keep it as further safety.
|
||||
return opts, fmt.Errorf("unsupported topologymanager option: %q (%s)", name, value)
|
||||
}
|
||||
}
|
||||
return opts, nil
|
||||
}
|
47
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_restricted.go
generated
vendored
Normal file
47
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_restricted.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
type restrictedPolicy struct {
|
||||
bestEffortPolicy
|
||||
}
|
||||
|
||||
var _ Policy = &restrictedPolicy{}
|
||||
|
||||
// PolicyRestricted policy name.
|
||||
const PolicyRestricted string = "restricted"
|
||||
|
||||
// NewRestrictedPolicy returns restricted policy.
|
||||
func NewRestrictedPolicy(numaInfo *NUMAInfo, opts PolicyOptions) Policy {
|
||||
return &restrictedPolicy{bestEffortPolicy{numaInfo: numaInfo, opts: opts}}
|
||||
}
|
||||
|
||||
func (p *restrictedPolicy) Name() string {
|
||||
return PolicyRestricted
|
||||
}
|
||||
|
||||
func (p *restrictedPolicy) canAdmitPodResult(hint *TopologyHint) bool {
|
||||
return hint.Preferred
|
||||
}
|
||||
|
||||
func (p *restrictedPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
|
||||
filteredHints := filterProvidersHints(providersHints)
|
||||
merger := NewHintMerger(p.numaInfo, filteredHints, p.Name(), p.opts)
|
||||
bestHint := merger.Merge()
|
||||
admit := p.canAdmitPodResult(&bestHint)
|
||||
return bestHint, admit
|
||||
}
|
75
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_single_numa_node.go
generated
vendored
Normal file
75
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/policy_single_numa_node.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
type singleNumaNodePolicy struct {
|
||||
// numaInfo represents list of NUMA Nodes available on the underlying machine and distances between them
|
||||
numaInfo *NUMAInfo
|
||||
opts PolicyOptions
|
||||
}
|
||||
|
||||
var _ Policy = &singleNumaNodePolicy{}
|
||||
|
||||
// PolicySingleNumaNode policy name.
|
||||
const PolicySingleNumaNode string = "single-numa-node"
|
||||
|
||||
// NewSingleNumaNodePolicy returns single-numa-node policy.
|
||||
func NewSingleNumaNodePolicy(numaInfo *NUMAInfo, opts PolicyOptions) Policy {
|
||||
return &singleNumaNodePolicy{numaInfo: numaInfo, opts: opts}
|
||||
}
|
||||
|
||||
func (p *singleNumaNodePolicy) Name() string {
|
||||
return PolicySingleNumaNode
|
||||
}
|
||||
|
||||
func (p *singleNumaNodePolicy) canAdmitPodResult(hint *TopologyHint) bool {
|
||||
return hint.Preferred
|
||||
}
|
||||
|
||||
// Return hints that have valid bitmasks with exactly one bit set.
|
||||
func filterSingleNumaHints(allResourcesHints [][]TopologyHint) [][]TopologyHint {
|
||||
var filteredResourcesHints [][]TopologyHint
|
||||
for _, oneResourceHints := range allResourcesHints {
|
||||
var filtered []TopologyHint
|
||||
for _, hint := range oneResourceHints {
|
||||
if hint.NUMANodeAffinity == nil && hint.Preferred {
|
||||
filtered = append(filtered, hint)
|
||||
}
|
||||
if hint.NUMANodeAffinity != nil && hint.NUMANodeAffinity.Count() == 1 && hint.Preferred {
|
||||
filtered = append(filtered, hint)
|
||||
}
|
||||
}
|
||||
filteredResourcesHints = append(filteredResourcesHints, filtered)
|
||||
}
|
||||
return filteredResourcesHints
|
||||
}
|
||||
|
||||
func (p *singleNumaNodePolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
|
||||
filteredHints := filterProvidersHints(providersHints)
|
||||
// Filter to only include don't cares and hints with a single NUMA node.
|
||||
singleNumaHints := filterSingleNumaHints(filteredHints)
|
||||
|
||||
merger := NewHintMerger(p.numaInfo, singleNumaHints, p.Name(), p.opts)
|
||||
bestHint := merger.Merge()
|
||||
|
||||
if bestHint.NUMANodeAffinity.IsEqual(p.numaInfo.DefaultAffinityMask()) {
|
||||
bestHint = TopologyHint{nil, bestHint.Preferred}
|
||||
}
|
||||
|
||||
admit := p.canAdmitPodResult(&bestHint)
|
||||
return bestHint, admit
|
||||
}
|
158
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope.go
generated
vendored
Normal file
158
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/admission"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
)
|
||||
|
||||
const (
|
||||
// containerTopologyScope specifies the TopologyManagerScope per container.
|
||||
containerTopologyScope = "container"
|
||||
// podTopologyScope specifies the TopologyManagerScope per pod.
|
||||
podTopologyScope = "pod"
|
||||
// noneTopologyScope specifies the TopologyManagerScope when topologyPolicyName is none.
|
||||
noneTopologyScope = "none"
|
||||
)
|
||||
|
||||
type podTopologyHints map[string]map[string]TopologyHint
|
||||
|
||||
// Scope interface for Topology Manager
|
||||
type Scope interface {
|
||||
Name() string
|
||||
GetPolicy() Policy
|
||||
Admit(pod *v1.Pod) lifecycle.PodAdmitResult
|
||||
// AddHintProvider adds a hint provider to manager to indicate the hint provider
|
||||
// wants to be consoluted with when making topology hints
|
||||
AddHintProvider(h HintProvider)
|
||||
// AddContainer adds pod to Manager for tracking
|
||||
AddContainer(pod *v1.Pod, container *v1.Container, containerID string)
|
||||
// RemoveContainer removes pod from Manager tracking
|
||||
RemoveContainer(containerID string) error
|
||||
// Store is the interface for storing pod topology hints
|
||||
Store
|
||||
}
|
||||
|
||||
type scope struct {
|
||||
mutex sync.Mutex
|
||||
name string
|
||||
// Mapping of a Pods mapping of Containers and their TopologyHints
|
||||
// Indexed by PodUID to ContainerName
|
||||
podTopologyHints podTopologyHints
|
||||
// The list of components registered with the Manager
|
||||
hintProviders []HintProvider
|
||||
// Topology Manager Policy
|
||||
policy Policy
|
||||
// Mapping of (PodUid, ContainerName) to ContainerID for Adding/Removing Pods from PodTopologyHints mapping
|
||||
podMap containermap.ContainerMap
|
||||
}
|
||||
|
||||
func (s *scope) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *scope) getTopologyHints(podUID string, containerName string) TopologyHint {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.podTopologyHints[podUID][containerName]
|
||||
}
|
||||
|
||||
func (s *scope) setTopologyHints(podUID string, containerName string, th TopologyHint) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.podTopologyHints[podUID] == nil {
|
||||
s.podTopologyHints[podUID] = make(map[string]TopologyHint)
|
||||
}
|
||||
s.podTopologyHints[podUID][containerName] = th
|
||||
}
|
||||
|
||||
func (s *scope) GetAffinity(podUID string, containerName string) TopologyHint {
|
||||
return s.getTopologyHints(podUID, containerName)
|
||||
}
|
||||
|
||||
func (s *scope) GetPolicy() Policy {
|
||||
return s.policy
|
||||
}
|
||||
|
||||
func (s *scope) AddHintProvider(h HintProvider) {
|
||||
s.hintProviders = append(s.hintProviders, h)
|
||||
}
|
||||
|
||||
// It would be better to implement this function in topologymanager instead of scope
|
||||
// but topologymanager do not track mapping anymore
|
||||
func (s *scope) AddContainer(pod *v1.Pod, container *v1.Container, containerID string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.podMap.Add(string(pod.UID), container.Name, containerID)
|
||||
}
|
||||
|
||||
// It would be better to implement this function in topologymanager instead of scope
|
||||
// but topologymanager do not track mapping anymore
|
||||
func (s *scope) RemoveContainer(containerID string) error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
klog.InfoS("RemoveContainer", "containerID", containerID)
|
||||
// Get the podUID and containerName associated with the containerID to be removed and remove it
|
||||
podUIDString, containerName, err := s.podMap.GetContainerRef(containerID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
s.podMap.RemoveByContainerID(containerID)
|
||||
|
||||
// In cases where a container has been restarted, it's possible that the same podUID and
|
||||
// containerName are already associated with a *different* containerID now. Only remove
|
||||
// the TopologyHints associated with that podUID and containerName if this is not true
|
||||
if _, err := s.podMap.GetContainerID(podUIDString, containerName); err != nil {
|
||||
delete(s.podTopologyHints[podUIDString], containerName)
|
||||
if len(s.podTopologyHints[podUIDString]) == 0 {
|
||||
delete(s.podTopologyHints, podUIDString)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *scope) admitPolicyNone(pod *v1.Pod) lifecycle.PodAdmitResult {
|
||||
for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) {
|
||||
err := s.allocateAlignedResources(pod, &container)
|
||||
if err != nil {
|
||||
return admission.GetPodAdmitResult(err)
|
||||
}
|
||||
}
|
||||
return admission.GetPodAdmitResult(nil)
|
||||
}
|
||||
|
||||
// It would be better to implement this function in topologymanager instead of scope
|
||||
// but topologymanager do not track providers anymore
|
||||
func (s *scope) allocateAlignedResources(pod *v1.Pod, container *v1.Container) error {
|
||||
for _, provider := range s.hintProviders {
|
||||
err := provider.Allocate(pod, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_container.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_container.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/admission"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
)
|
||||
|
||||
type containerScope struct {
|
||||
scope
|
||||
}
|
||||
|
||||
// Ensure containerScope implements Scope interface
|
||||
var _ Scope = &containerScope{}
|
||||
|
||||
// NewContainerScope returns a container scope.
|
||||
func NewContainerScope(policy Policy) Scope {
|
||||
return &containerScope{
|
||||
scope{
|
||||
name: containerTopologyScope,
|
||||
podTopologyHints: podTopologyHints{},
|
||||
policy: policy,
|
||||
podMap: containermap.NewContainerMap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *containerScope) Admit(pod *v1.Pod) lifecycle.PodAdmitResult {
|
||||
for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) {
|
||||
bestHint, admit := s.calculateAffinity(pod, &container)
|
||||
klog.InfoS("Best TopologyHint", "bestHint", bestHint, "pod", klog.KObj(pod), "containerName", container.Name)
|
||||
|
||||
if !admit {
|
||||
metrics.TopologyManagerAdmissionErrorsTotal.Inc()
|
||||
return admission.GetPodAdmitResult(&TopologyAffinityError{})
|
||||
}
|
||||
klog.InfoS("Topology Affinity", "bestHint", bestHint, "pod", klog.KObj(pod), "containerName", container.Name)
|
||||
s.setTopologyHints(string(pod.UID), container.Name, bestHint)
|
||||
|
||||
err := s.allocateAlignedResources(pod, &container)
|
||||
if err != nil {
|
||||
metrics.TopologyManagerAdmissionErrorsTotal.Inc()
|
||||
return admission.GetPodAdmitResult(err)
|
||||
}
|
||||
|
||||
if IsAlignmentGuaranteed(s.policy) {
|
||||
metrics.ContainerAlignedComputeResources.WithLabelValues(metrics.AlignScopeContainer, metrics.AlignedNUMANode).Inc()
|
||||
}
|
||||
}
|
||||
return admission.GetPodAdmitResult(nil)
|
||||
}
|
||||
|
||||
func (s *containerScope) accumulateProvidersHints(pod *v1.Pod, container *v1.Container) []map[string][]TopologyHint {
|
||||
var providersHints []map[string][]TopologyHint
|
||||
|
||||
for _, provider := range s.hintProviders {
|
||||
// Get the TopologyHints for a Container from a provider.
|
||||
hints := provider.GetTopologyHints(pod, container)
|
||||
providersHints = append(providersHints, hints)
|
||||
klog.InfoS("TopologyHints", "hints", hints, "pod", klog.KObj(pod), "containerName", container.Name)
|
||||
}
|
||||
return providersHints
|
||||
}
|
||||
|
||||
func (s *containerScope) calculateAffinity(pod *v1.Pod, container *v1.Container) (TopologyHint, bool) {
|
||||
providersHints := s.accumulateProvidersHints(pod, container)
|
||||
bestHint, admit := s.policy.Merge(providersHints)
|
||||
klog.InfoS("ContainerTopologyHint", "bestHint", bestHint)
|
||||
return bestHint, admit
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_none.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_none.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
)
|
||||
|
||||
type noneScope struct {
|
||||
scope
|
||||
}
|
||||
|
||||
// Ensure noneScope implements Scope interface
|
||||
var _ Scope = &noneScope{}
|
||||
|
||||
// NewNoneScope returns a none scope.
|
||||
func NewNoneScope() Scope {
|
||||
return &noneScope{
|
||||
scope{
|
||||
name: noneTopologyScope,
|
||||
podTopologyHints: podTopologyHints{},
|
||||
policy: NewNonePolicy(),
|
||||
podMap: containermap.NewContainerMap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *noneScope) Admit(pod *v1.Pod) lifecycle.PodAdmitResult {
|
||||
return s.admitPolicyNone(pod)
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_pod.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/scope_pod.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/admission"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
)
|
||||
|
||||
type podScope struct {
|
||||
scope
|
||||
}
|
||||
|
||||
// Ensure podScope implements Scope interface
|
||||
var _ Scope = &podScope{}
|
||||
|
||||
// NewPodScope returns a pod scope.
|
||||
func NewPodScope(policy Policy) Scope {
|
||||
return &podScope{
|
||||
scope{
|
||||
name: podTopologyScope,
|
||||
podTopologyHints: podTopologyHints{},
|
||||
policy: policy,
|
||||
podMap: containermap.NewContainerMap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *podScope) Admit(pod *v1.Pod) lifecycle.PodAdmitResult {
|
||||
bestHint, admit := s.calculateAffinity(pod)
|
||||
klog.InfoS("Best TopologyHint", "bestHint", bestHint, "pod", klog.KObj(pod))
|
||||
if !admit {
|
||||
metrics.TopologyManagerAdmissionErrorsTotal.Inc()
|
||||
return admission.GetPodAdmitResult(&TopologyAffinityError{})
|
||||
}
|
||||
|
||||
for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) {
|
||||
klog.InfoS("Topology Affinity", "bestHint", bestHint, "pod", klog.KObj(pod), "containerName", container.Name)
|
||||
s.setTopologyHints(string(pod.UID), container.Name, bestHint)
|
||||
|
||||
err := s.allocateAlignedResources(pod, &container)
|
||||
if err != nil {
|
||||
metrics.TopologyManagerAdmissionErrorsTotal.Inc()
|
||||
return admission.GetPodAdmitResult(err)
|
||||
}
|
||||
}
|
||||
if IsAlignmentGuaranteed(s.policy) {
|
||||
// increment only if we know we allocate aligned resources.
|
||||
metrics.ContainerAlignedComputeResources.WithLabelValues(metrics.AlignScopePod, metrics.AlignedNUMANode).Inc()
|
||||
}
|
||||
return admission.GetPodAdmitResult(nil)
|
||||
}
|
||||
|
||||
func (s *podScope) accumulateProvidersHints(pod *v1.Pod) []map[string][]TopologyHint {
|
||||
var providersHints []map[string][]TopologyHint
|
||||
|
||||
for _, provider := range s.hintProviders {
|
||||
// Get the TopologyHints for a Pod from a provider.
|
||||
hints := provider.GetPodTopologyHints(pod)
|
||||
providersHints = append(providersHints, hints)
|
||||
klog.InfoS("TopologyHints", "hints", hints, "pod", klog.KObj(pod))
|
||||
}
|
||||
return providersHints
|
||||
}
|
||||
|
||||
func (s *podScope) calculateAffinity(pod *v1.Pod) (TopologyHint, bool) {
|
||||
providersHints := s.accumulateProvidersHints(pod)
|
||||
bestHint, admit := s.policy.Merge(providersHints)
|
||||
klog.InfoS("PodTopologyHint", "bestHint", bestHint)
|
||||
return bestHint, admit
|
||||
}
|
222
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/topology_manager.go
generated
vendored
Normal file
222
vendor/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/topology_manager.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package topologymanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultMaxAllowableNUMANodes specifies the maximum number of NUMA Nodes that
|
||||
// the TopologyManager supports on the underlying machine.
|
||||
//
|
||||
// At present, having more than this number of NUMA Nodes will result in a
|
||||
// state explosion when trying to enumerate possible NUMAAffinity masks and
|
||||
// generate hints for them. As such, if more NUMA Nodes than this are
|
||||
// present on a machine and the TopologyManager is enabled, an error will
|
||||
// be returned and the TopologyManager will not be loaded.
|
||||
defaultMaxAllowableNUMANodes = 8
|
||||
// ErrorTopologyAffinity represents the type for a TopologyAffinityError
|
||||
ErrorTopologyAffinity = "TopologyAffinityError"
|
||||
)
|
||||
|
||||
// TopologyAffinityError represents an resource alignment error
|
||||
type TopologyAffinityError struct{}
|
||||
|
||||
func (e TopologyAffinityError) Error() string {
|
||||
return "Resources cannot be allocated with Topology locality"
|
||||
}
|
||||
|
||||
func (e TopologyAffinityError) Type() string {
|
||||
return ErrorTopologyAffinity
|
||||
}
|
||||
|
||||
// Manager interface provides methods for Kubelet to manage pod topology hints
|
||||
type Manager interface {
|
||||
// PodAdmitHandler is implemented by Manager
|
||||
lifecycle.PodAdmitHandler
|
||||
// AddHintProvider adds a hint provider to manager to indicate the hint provider
|
||||
// wants to be consulted with when making topology hints
|
||||
AddHintProvider(HintProvider)
|
||||
// AddContainer adds pod to Manager for tracking
|
||||
AddContainer(pod *v1.Pod, container *v1.Container, containerID string)
|
||||
// RemoveContainer removes pod from Manager tracking
|
||||
RemoveContainer(containerID string) error
|
||||
// Store is the interface for storing pod topology hints
|
||||
Store
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
//Topology Manager Scope
|
||||
scope Scope
|
||||
}
|
||||
|
||||
// HintProvider is an interface for components that want to collaborate to
|
||||
// achieve globally optimal concrete resource alignment with respect to
|
||||
// NUMA locality.
|
||||
type HintProvider interface {
|
||||
// GetTopologyHints returns a map of resource names to a list of possible
|
||||
// concrete resource allocations in terms of NUMA locality hints. Each hint
|
||||
// is optionally marked "preferred" and indicates the set of NUMA nodes
|
||||
// involved in the hypothetical allocation. The topology manager calls
|
||||
// this function for each hint provider, and merges the hints to produce
|
||||
// a consensus "best" hint. The hint providers may subsequently query the
|
||||
// topology manager to influence actual resource assignment.
|
||||
GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint
|
||||
// GetPodTopologyHints returns a map of resource names to a list of possible
|
||||
// concrete resource allocations per Pod in terms of NUMA locality hints.
|
||||
GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint
|
||||
// Allocate triggers resource allocation to occur on the HintProvider after
|
||||
// all hints have been gathered and the aggregated Hint is available via a
|
||||
// call to Store.GetAffinity().
|
||||
Allocate(pod *v1.Pod, container *v1.Container) error
|
||||
}
|
||||
|
||||
// Store interface is to allow Hint Providers to retrieve pod affinity
|
||||
type Store interface {
|
||||
GetAffinity(podUID string, containerName string) TopologyHint
|
||||
GetPolicy() Policy
|
||||
}
|
||||
|
||||
// TopologyHint is a struct containing the NUMANodeAffinity for a Container
|
||||
type TopologyHint struct {
|
||||
NUMANodeAffinity bitmask.BitMask
|
||||
// Preferred is set to true when the NUMANodeAffinity encodes a preferred
|
||||
// allocation for the Container. It is set to false otherwise.
|
||||
Preferred bool
|
||||
}
|
||||
|
||||
// IsEqual checks if TopologyHint are equal
|
||||
func (th *TopologyHint) IsEqual(topologyHint TopologyHint) bool {
|
||||
if th.Preferred == topologyHint.Preferred {
|
||||
if th.NUMANodeAffinity == nil || topologyHint.NUMANodeAffinity == nil {
|
||||
return th.NUMANodeAffinity == topologyHint.NUMANodeAffinity
|
||||
}
|
||||
return th.NUMANodeAffinity.IsEqual(topologyHint.NUMANodeAffinity)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LessThan checks if TopologyHint `a` is less than TopologyHint `b`
|
||||
// this means that either `a` is a preferred hint and `b` is not
|
||||
// or `a` NUMANodeAffinity attribute is narrower than `b` NUMANodeAffinity attribute.
|
||||
func (th *TopologyHint) LessThan(other TopologyHint) bool {
|
||||
if th.Preferred != other.Preferred {
|
||||
return th.Preferred
|
||||
}
|
||||
return th.NUMANodeAffinity.IsNarrowerThan(other.NUMANodeAffinity)
|
||||
}
|
||||
|
||||
var _ Manager = &manager{}
|
||||
|
||||
// NewManager creates a new TopologyManager based on provided policy and scope
|
||||
func NewManager(topology []cadvisorapi.Node, topologyPolicyName string, topologyScopeName string, topologyPolicyOptions map[string]string) (Manager, error) {
|
||||
// When policy is none, the scope is not relevant, so we can short circuit here.
|
||||
if topologyPolicyName == PolicyNone {
|
||||
klog.InfoS("Creating topology manager with none policy")
|
||||
return &manager{scope: NewNoneScope()}, nil
|
||||
}
|
||||
|
||||
opts, err := NewPolicyOptions(topologyPolicyOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klog.InfoS("Creating topology manager with policy per scope", "topologyPolicyName", topologyPolicyName, "topologyScopeName", topologyScopeName, "topologyPolicyOptions", opts)
|
||||
|
||||
numaInfo, err := NewNUMAInfo(topology, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot discover NUMA topology: %w", err)
|
||||
}
|
||||
|
||||
if topologyPolicyName != PolicyNone && len(numaInfo.Nodes) > opts.MaxAllowableNUMANodes {
|
||||
return nil, fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", opts.MaxAllowableNUMANodes)
|
||||
}
|
||||
|
||||
var policy Policy
|
||||
switch topologyPolicyName {
|
||||
|
||||
case PolicyBestEffort:
|
||||
policy = NewBestEffortPolicy(numaInfo, opts)
|
||||
|
||||
case PolicyRestricted:
|
||||
policy = NewRestrictedPolicy(numaInfo, opts)
|
||||
|
||||
case PolicySingleNumaNode:
|
||||
policy = NewSingleNumaNodePolicy(numaInfo, opts)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName)
|
||||
}
|
||||
|
||||
var scope Scope
|
||||
switch topologyScopeName {
|
||||
|
||||
case containerTopologyScope:
|
||||
scope = NewContainerScope(policy)
|
||||
|
||||
case podTopologyScope:
|
||||
scope = NewPodScope(policy)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown scope: \"%s\"", topologyScopeName)
|
||||
}
|
||||
|
||||
manager := &manager{
|
||||
scope: scope,
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetAffinity(podUID string, containerName string) TopologyHint {
|
||||
return m.scope.GetAffinity(podUID, containerName)
|
||||
}
|
||||
|
||||
func (m *manager) GetPolicy() Policy {
|
||||
return m.scope.GetPolicy()
|
||||
}
|
||||
|
||||
func (m *manager) AddHintProvider(h HintProvider) {
|
||||
m.scope.AddHintProvider(h)
|
||||
}
|
||||
|
||||
func (m *manager) AddContainer(pod *v1.Pod, container *v1.Container, containerID string) {
|
||||
m.scope.AddContainer(pod, container, containerID)
|
||||
}
|
||||
|
||||
func (m *manager) RemoveContainer(containerID string) error {
|
||||
return m.scope.RemoveContainer(containerID)
|
||||
}
|
||||
|
||||
func (m *manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
|
||||
metrics.TopologyManagerAdmissionRequestsTotal.Inc()
|
||||
|
||||
startTime := time.Now()
|
||||
podAdmitResult := m.scope.Admit(attrs.Pod)
|
||||
metrics.TopologyManagerAdmissionDuration.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
|
||||
return podAdmitResult
|
||||
}
|
Reference in New Issue
Block a user