Update TopologyManager single-numa-node logic to handle "don't cares"

The logic has been updated to match the logic of the best-effort policy
except in two places:

1) The hint filtering frunction has been updated to allow "don't care"
hints encoded with a `nil` affinity mask, to pass through the filter in
addition to hints that have just a single NUMA bit set.

2) After calculating the `bestHint` we transform "don't care" affinities
encoded as having all NUMA bits set in their affinity masks into "don't
care" affinities encoded as `nil`.
This commit is contained in:
Kevin Klues 2020-01-16 15:54:17 +01:00 committed by nolancon
parent 2905ffffa7
commit 7069b1d6e8
2 changed files with 52 additions and 43 deletions

View File

@ -18,6 +18,7 @@ package topologymanager
import ( import (
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
"k8s.io/kubernetes/pkg/kubelet/lifecycle" "k8s.io/kubernetes/pkg/kubelet/lifecycle"
) )
@ -59,95 +60,100 @@ func (p *singleNumaNodePolicy) filterHints(allResourcesHints [][]TopologyHint) [
for _, oneResourceHints := range allResourcesHints { for _, oneResourceHints := range allResourcesHints {
var filtered []TopologyHint var filtered []TopologyHint
for _, hint := range oneResourceHints { for _, hint := range oneResourceHints {
if hint.NUMANodeAffinity == nil && hint.Preferred == true {
filtered = append(filtered, hint)
}
if hint.NUMANodeAffinity != nil && hint.NUMANodeAffinity.Count() == 1 && hint.Preferred == true { if hint.NUMANodeAffinity != nil && hint.NUMANodeAffinity.Count() == 1 && hint.Preferred == true {
filtered = append(filtered, hint) filtered = append(filtered, hint)
} }
} }
filteredResourcesHints = append(filteredResourcesHints, filtered) filteredResourcesHints = append(filteredResourcesHints, filtered)
} }
return filteredResourcesHints return filteredResourcesHints
} }
func (p *singleNumaNodePolicy) mergeProvidersHints(providersHints []map[string][]TopologyHint) TopologyHint { func (p *singleNumaNodePolicy) mergeProvidersHints(providersHints []map[string][]TopologyHint) TopologyHint {
// Set the default affinity as an any-numa affinity containing the list
// of NUMA Nodes available on this machine.
defaultAffinity, _ := bitmask.NewBitMask(p.numaNodes...)
// Loop through all provider hints and save an accumulated list of the // Loop through all provider hints and save an accumulated list of the
// hints returned by each hint provider. If no hints are provided, assume // hints returned by each hint provider. If no hints are provided, assume
// that provider has no preference for topology-aware allocation. // that provider has no preference for topology-aware allocation.
var allResourcesHints [][]TopologyHint var allProviderHints [][]TopologyHint
for _, hints := range providersHints { for _, hints := range providersHints {
// If hints is nil, insert a single, preferred any-numa hint into allProviderHints.
if len(hints) == 0 { if len(hints) == 0 {
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource, " + klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource")
"skipping.") allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})
continue continue
} }
// Otherwise, accumulate the hints for each resource type into allProviderHints. // Otherwise, accumulate the hints for each resource type into allProviderHints.
for resource := range hints { for resource := range hints {
if hints[resource] == nil { if hints[resource] == nil {
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource "+ klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource)
"'%s', skipping.", resource) allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})
continue continue
} }
if len(hints[resource]) == 0 { if len(hints[resource]) == 0 {
klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource)
resource) allProviderHints = append(allProviderHints, []TopologyHint{{nil, false}})
// return defaultHint which will fail pod admission continue
return TopologyHint{}
} }
klog.Infof("[topologymanager] TopologyHints for resource '%v': %v", resource, hints[resource])
allResourcesHints = append(allResourcesHints, hints[resource]) allProviderHints = append(allProviderHints, hints[resource])
} }
} }
// In case allProviderHints length is zero it means that we have no
// preference for NUMA affinity. In that case update default hint preferred
// to true to allow scheduling.
if len(allResourcesHints) == 0 {
klog.Infof("[topologymanager] No preference for NUMA affinity from all providers")
return TopologyHint{nil, true}
}
allResourcesHints = p.filterHints(allResourcesHints) // Filter to only include don't cares and hints with a single NUMA node.
// If no hints, or there is a resource with empty hints after filtering, then policy allProviderHints = p.filterHints(allProviderHints)
// cannot be satisfied
if len(allResourcesHints) == 0 {
klog.Infof("[topologymanager] No hints that align to a single NUMA node.")
return TopologyHint{}
}
for _, hints := range allResourcesHints {
if len(hints) == 0 {
klog.Infof("[topologymanager] No hints that align to a single NUMA node for resource.")
return TopologyHint{}
}
}
// Set the bestHint to return from this function as {nil false}. // Set the bestHint to return from this function as {nil false}.
// This will only be returned if no better hint can be found when // This will only be returned if no better hint can be found when
// merging hints from each hint provider. // merging hints from each hint provider.
bestHint := TopologyHint{} bestHint := TopologyHint{defaultAffinity, false}
iterateAllProviderTopologyHints(allResourcesHints, func(permutation []TopologyHint) { iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) {
// Get the NUMANodeAffinity from each hint in the permutation and see if any
// of them encode unpreferred allocations.
mergedHint := mergePermutation(p.numaNodes, permutation) mergedHint := mergePermutation(p.numaNodes, permutation)
// Only consider mergedHints that result in a NUMANodeAffinity == 1 to
// replace the current defaultHint. // Only consider mergedHints that result in a NUMANodeAffinity > 0 to
if mergedHint.NUMANodeAffinity.Count() != 1 { // replace the current bestHint.
if mergedHint.NUMANodeAffinity.Count() == 0 {
return return
} }
// If the current bestHint NUMANodeAffinity is nil, update bestHint // If the current bestHint is non-preferred and the new mergedHint is
// to the current mergedHint. // preferred, always choose the preferred hint over the non-preferred one.
if bestHint.NUMANodeAffinity == nil { if mergedHint.Preferred && !bestHint.Preferred {
bestHint = mergedHint bestHint = mergedHint
return return
} }
// Only consider mergedHints that have a narrower NUMANodeAffinity // If the current bestHint is preferred and the new mergedHint is
// than the NUMANodeAffinity in the current bestHint. // non-preferred, never update bestHint, regardless of mergedHint's
// narowness.
if !mergedHint.Preferred && bestHint.Preferred {
return
}
// If mergedHint and bestHint has the same preference, only consider
// mergedHints that have a narrower NUMANodeAffinity than the
// NUMANodeAffinity in the current bestHint.
if !mergedHint.NUMANodeAffinity.IsNarrowerThan(bestHint.NUMANodeAffinity) { if !mergedHint.NUMANodeAffinity.IsNarrowerThan(bestHint.NUMANodeAffinity) {
return return
} }
// In all other cases, update bestHint to the current mergedHint // In all other cases, update bestHint to the current mergedHint
bestHint = mergedHint bestHint = mergedHint
}) })
if bestHint.NUMANodeAffinity.IsEqual(defaultAffinity) {
bestHint = TopologyHint{nil, bestHint.Preferred}
}
return bestHint return bestHint
} }

View File

@ -77,7 +77,9 @@ func TestPolicySingleNumaNodeFilterHints(t *testing.T) {
}, },
expectedResources: [][]TopologyHint{ expectedResources: [][]TopologyHint{
[]TopologyHint(nil), []TopologyHint(nil),
[]TopologyHint(nil), {
{NUMANodeAffinity: nil, Preferred: true},
},
}, },
}, },
{ {
@ -98,6 +100,7 @@ func TestPolicySingleNumaNodeFilterHints(t *testing.T) {
}, },
{ {
{NUMANodeAffinity: NewTestBitMask(1), Preferred: true}, {NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
{NUMANodeAffinity: nil, Preferred: true},
}, },
}, },
}, },