Skip to content

Commit 7d4d175

Browse files
authored
Merge pull request kubernetes#81722 from klueska/upstream-add-socket-awreness-to-topologymanager
Add NUMA Node awareness to the TopologyManager
2 parents d7ec9f7 + df1b54f commit 7d4d175

File tree

4 files changed

+66
-43
lines changed

4 files changed

+66
-43
lines changed

pkg/kubelet/cm/container_manager_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
289289

290290
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManager) {
291291
cm.topologyManager, err = topologymanager.NewManager(
292+
numaNodeInfo,
292293
nodeConfig.ExperimentalTopologyManagerPolicy,
293294
)
294295

pkg/kubelet/cm/topologymanager/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ go_library(
1313
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager",
1414
visibility = ["//visibility:public"],
1515
deps = [
16+
"//pkg/kubelet/cm/cpumanager/topology:go_default_library",
1617
"//pkg/kubelet/cm/topologymanager/socketmask:go_default_library",
1718
"//pkg/kubelet/lifecycle:go_default_library",
1819
"//staging/src/k8s.io/api/core/v1:go_default_library",

pkg/kubelet/cm/topologymanager/topology_manager.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,23 @@ import (
2121

2222
"k8s.io/api/core/v1"
2323
"k8s.io/klog"
24+
cputopology "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
2425
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/socketmask"
2526
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
2627
)
2728

29+
const (
30+
// maxAllowableNUMANodes specifies the maximum number of NUMA Nodes that
31+
// the TopologyManager supports on the underlying machine.
32+
//
33+
// At present, having more than this number of NUMA Nodes will result in a
34+
// state explosion when trying to enumerate possible NUMAAffinity masks and
35+
// generate hints for them. As such, if more NUMA Nodes than this are
36+
// present on a machine and the TopologyManager is enabled, an error will
37+
// be returned and the TopologyManager will not be loaded.
38+
maxAllowableNUMANodes = 8
39+
)
40+
2841
//Manager interface provides methods for Kubelet to manage pod topology hints
2942
type Manager interface {
3043
//Manager implements pod admit handler interface
@@ -50,6 +63,8 @@ type manager struct {
5063
podMap map[string]string
5164
//Topology Manager Policy
5265
policy Policy
66+
//List of NUMA Nodes available on the underlying machine
67+
numaNodes []int
5368
}
5469

5570
//HintProvider interface is to be implemented by Hint Providers
@@ -73,7 +88,7 @@ type TopologyHint struct {
7388
var _ Manager = &manager{}
7489

7590
//NewManager creates a new TopologyManager based on provided policy
76-
func NewManager(topologyPolicyName string) (Manager, error) {
91+
func NewManager(numaNodeInfo cputopology.NUMANodeInfo, topologyPolicyName string) (Manager, error) {
7792
klog.Infof("[topologymanager] Creating topology manager with %s policy", topologyPolicyName)
7893
var policy Policy
7994

@@ -92,6 +107,15 @@ func NewManager(topologyPolicyName string) (Manager, error) {
92107
return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName)
93108
}
94109

110+
var numaNodes []int
111+
for node := range numaNodeInfo {
112+
numaNodes = append(numaNodes, node)
113+
}
114+
115+
if len(numaNodes) > maxAllowableNUMANodes {
116+
return nil, fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", maxAllowableNUMANodes)
117+
}
118+
95119
var hp []HintProvider
96120
pth := make(map[string]map[string]TopologyHint)
97121
pm := make(map[string]string)
@@ -100,6 +124,7 @@ func NewManager(topologyPolicyName string) (Manager, error) {
100124
podTopologyHints: pth,
101125
podMap: pm,
102126
policy: policy,
127+
numaNodes: numaNodes,
103128
}
104129

105130
return manager, nil
@@ -149,12 +174,9 @@ func (m *manager) iterateAllProviderTopologyHints(allProviderHints [][]TopologyH
149174

150175
// Merge the hints from all hint providers to find the best one.
151176
func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) TopologyHint {
152-
// Set the default hint to return from this function as an any-numa
153-
// affinity with an unpreferred allocation. This will only be returned if
154-
// no better hint can be found when merging hints from each hint provider.
155-
defaultAffinity, _ := socketmask.NewSocketMask()
156-
defaultAffinity.Fill()
157-
defaultHint := TopologyHint{defaultAffinity, false}
177+
// Set the default affinity as an any-numa affinity containing the list
178+
// of NUMA Nodes available on this machine.
179+
defaultAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
158180

159181
// Loop through all hint providers and save an accumulated list of the
160182
// hints returned by each hint provider. If no hints are provided, assume
@@ -167,27 +189,21 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
167189
// If hints is nil, insert a single, preferred any-numa hint into allProviderHints.
168190
if len(hints) == 0 {
169191
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource")
170-
affinity, _ := socketmask.NewSocketMask()
171-
affinity.Fill()
172-
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
192+
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
173193
continue
174194
}
175195

176196
// Otherwise, accumulate the hints for each resource type into allProviderHints.
177197
for resource := range hints {
178198
if hints[resource] == nil {
179199
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource)
180-
affinity, _ := socketmask.NewSocketMask()
181-
affinity.Fill()
182-
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
200+
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
183201
continue
184202
}
185203

186204
if len(hints[resource]) == 0 {
187205
klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource)
188-
affinity, _ := socketmask.NewSocketMask()
189-
affinity.Fill()
190-
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, false}})
206+
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, false}})
191207
continue
192208
}
193209

@@ -199,8 +215,8 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
199215
// hints in each permutation by taking the bitwise-and of their affinity masks.
200216
// Return the hint with the narrowest NUMANodeAffinity of all merged
201217
// permutations that have at least one NUMA ID set. If no merged mask can be
202-
// found that has at least one NUMA ID set, return the 'defaultHint'.
203-
bestHint := defaultHint
218+
// found that has at least one NUMA ID set, return the 'defaultAffinity'.
219+
bestHint := TopologyHint{defaultAffinity, false}
204220
m.iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) {
205221
// Get the NUMANodeAffinity from each hint in the permutation and see if any
206222
// of them encode unpreferred allocations.
@@ -217,8 +233,7 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
217233
}
218234

219235
// Merge the affinities using a bitwise-and operation.
220-
mergedAffinity, _ := socketmask.NewSocketMask()
221-
mergedAffinity.Fill()
236+
mergedAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
222237
mergedAffinity.And(numaAffinities...)
223238

224239
// Build a mergedHintfrom the merged affinity mask, indicating if an

pkg/kubelet/cm/topologymanager/topology_manager_test.go

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ func NewTestSocketMask(sockets ...int) socketmask.SocketMask {
3333
return s
3434
}
3535

36-
func NewTestSocketMaskFull() socketmask.SocketMask {
37-
s, _ := socketmask.NewSocketMask()
38-
s.Fill()
39-
return s
40-
}
41-
4236
func TestNewManager(t *testing.T) {
4337
tcases := []struct {
4438
description string
@@ -64,7 +58,7 @@ func TestNewManager(t *testing.T) {
6458
}
6559

6660
for _, tc := range tcases {
67-
mngr, err := NewManager(tc.policyName)
61+
mngr, err := NewManager(nil, tc.policyName)
6862

6963
if tc.expectedError != nil {
7064
if !strings.Contains(err.Error(), tc.expectedError.Error()) {
@@ -111,6 +105,8 @@ func TestGetAffinity(t *testing.T) {
111105
}
112106

113107
func TestCalculateAffinity(t *testing.T) {
108+
numaNodes := []int{0, 1}
109+
114110
tcases := []struct {
115111
name string
116112
hp []HintProvider
@@ -120,7 +116,7 @@ func TestCalculateAffinity(t *testing.T) {
120116
name: "TopologyHint not set",
121117
hp: []HintProvider{},
122118
expected: TopologyHint{
123-
NUMANodeAffinity: NewTestSocketMaskFull(),
119+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
124120
Preferred: true,
125121
},
126122
},
@@ -132,7 +128,7 @@ func TestCalculateAffinity(t *testing.T) {
132128
},
133129
},
134130
expected: TopologyHint{
135-
NUMANodeAffinity: NewTestSocketMaskFull(),
131+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
136132
Preferred: true,
137133
},
138134
},
@@ -146,7 +142,7 @@ func TestCalculateAffinity(t *testing.T) {
146142
},
147143
},
148144
expected: TopologyHint{
149-
NUMANodeAffinity: NewTestSocketMaskFull(),
145+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
150146
Preferred: true,
151147
},
152148
},
@@ -160,7 +156,7 @@ func TestCalculateAffinity(t *testing.T) {
160156
},
161157
},
162158
expected: TopologyHint{
163-
NUMANodeAffinity: NewTestSocketMaskFull(),
159+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
164160
Preferred: false,
165161
},
166162
},
@@ -179,7 +175,7 @@ func TestCalculateAffinity(t *testing.T) {
179175
},
180176
},
181177
expected: TopologyHint{
182-
NUMANodeAffinity: NewTestSocketMaskFull(),
178+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
183179
Preferred: true,
184180
},
185181
},
@@ -198,7 +194,7 @@ func TestCalculateAffinity(t *testing.T) {
198194
},
199195
},
200196
expected: TopologyHint{
201-
NUMANodeAffinity: NewTestSocketMaskFull(),
197+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
202198
Preferred: true,
203199
},
204200
},
@@ -343,7 +339,7 @@ func TestCalculateAffinity(t *testing.T) {
343339
},
344340
},
345341
expected: TopologyHint{
346-
NUMANodeAffinity: NewTestSocketMaskFull(),
342+
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
347343
Preferred: false,
348344
},
349345
},
@@ -662,8 +658,10 @@ func TestCalculateAffinity(t *testing.T) {
662658
}
663659

664660
for _, tc := range tcases {
665-
mngr := manager{}
666-
mngr.hintProviders = tc.hp
661+
mngr := manager{
662+
hintProviders: tc.hp,
663+
numaNodes: numaNodes,
664+
}
667665
actual := mngr.calculateAffinity(v1.Pod{}, v1.Container{})
668666
if !actual.NUMANodeAffinity.IsEqual(tc.expected.NUMANodeAffinity) {
669667
t.Errorf("Expected NUMANodeAffinity in result to be %v, got %v", tc.expected.NUMANodeAffinity, actual.NUMANodeAffinity)
@@ -926,10 +924,13 @@ func TestAdmit(t *testing.T) {
926924
},
927925
}
928926
for _, tc := range tcases {
929-
man := manager{}
930-
man.policy = tc.policy
931-
man.podTopologyHints = make(map[string]map[string]TopologyHint)
932-
man.hintProviders = tc.hp
927+
man := manager{
928+
policy: tc.policy,
929+
podTopologyHints: make(map[string]map[string]TopologyHint),
930+
hintProviders: tc.hp,
931+
numaNodes: []int{0, 1},
932+
}
933+
933934
pod := &v1.Pod{
934935
Spec: v1.PodSpec{
935936
Containers: []v1.Container{
@@ -938,10 +939,15 @@ func TestAdmit(t *testing.T) {
938939
},
939940
},
940941
},
942+
Status: v1.PodStatus{
943+
QOSClass: tc.qosClass,
944+
},
941945
}
942-
podAttr := lifecycle.PodAdmitAttributes{}
943-
pod.Status.QOSClass = tc.qosClass
944-
podAttr.Pod = pod
946+
947+
podAttr := lifecycle.PodAdmitAttributes{
948+
Pod: pod,
949+
}
950+
945951
actual := man.Admit(&podAttr)
946952
if actual.Admit != tc.expected {
947953
t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, actual.Admit)

0 commit comments

Comments
 (0)