Avoid undesirable allocation when device is associated with multiple NUMA Nodes
suppose there are two devices dev1 and dev2, each has NUMA Nodes associated as below: dev1: numa1 dev2: numa1, numa2 and we request a device from numa2, currently filterByAffinity() will return [], [dev1, dev2], [] if loop of available devices produce a sequence of [dev1, dev2], that is is not desirable as what we truely expect is an allocation of dev2 from numa2.
This commit is contained in:
		@@ -791,9 +791,23 @@ func (m *ManagerImpl) filterByAffinity(podUID, contName, resource string, availa
 | 
				
			|||||||
		nodes = append(nodes, node)
 | 
							nodes = append(nodes, node)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Sort the list of nodes by how many devices they contain.
 | 
						// Sort the list of nodes by nodes from affinity set, nodes from none-affinity
 | 
				
			||||||
 | 
						// set, nodes without topology set and how many devices they contain.
 | 
				
			||||||
	sort.Slice(nodes, func(i, j int) bool {
 | 
						sort.Slice(nodes, func(i, j int) bool {
 | 
				
			||||||
		return perNodeDevices[i].Len() < perNodeDevices[j].Len()
 | 
							ni, nj := nodes[i], nodes[j]
 | 
				
			||||||
 | 
							// 1. node[i] from to affninity set
 | 
				
			||||||
 | 
							if hint.NUMANodeAffinity.IsSet(ni) {
 | 
				
			||||||
 | 
								return !hint.NUMANodeAffinity.IsSet(nj) || perNodeDevices[ni].Len() < perNodeDevices[nj].Len()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 2. node[i] from none-affinity set
 | 
				
			||||||
 | 
							if ni != nodeWithoutTopology {
 | 
				
			||||||
 | 
								return !hint.NUMANodeAffinity.IsSet(nj) &&
 | 
				
			||||||
 | 
									(nj == nodeWithoutTopology || perNodeDevices[ni].Len() < perNodeDevices[nj].Len())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 3. node[i] from node without topology set
 | 
				
			||||||
 | 
							return nj == nodeWithoutTopology && perNodeDevices[ni].Len() < perNodeDevices[nj].Len()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Generate three sorted lists of devices. Devices in the first list come
 | 
						// Generate three sorted lists of devices. Devices in the first list come
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
 | 
						"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/cm/devicemanager/checkpoint"
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/devicemanager/checkpoint"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/config"
 | 
						"k8s.io/kubernetes/pkg/kubelet/config"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
 | 
						"k8s.io/kubernetes/pkg/kubelet/lifecycle"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/pluginmanager"
 | 
						"k8s.io/kubernetes/pkg/kubelet/pluginmanager"
 | 
				
			||||||
@@ -669,6 +670,106 @@ type TestResource struct {
 | 
				
			|||||||
	topology         bool
 | 
						topology         bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFilterByAffinity(t *testing.T) {
 | 
				
			||||||
 | 
						as := require.New(t)
 | 
				
			||||||
 | 
						allDevices := ResourceDeviceInstances{
 | 
				
			||||||
 | 
							"res1": map[string]pluginapi.Device{
 | 
				
			||||||
 | 
								"dev1": {
 | 
				
			||||||
 | 
									ID: "dev1",
 | 
				
			||||||
 | 
									Topology: &pluginapi.TopologyInfo{
 | 
				
			||||||
 | 
										Nodes: []*pluginapi.NUMANode{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												ID: 1,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"dev2": {
 | 
				
			||||||
 | 
									ID: "dev2",
 | 
				
			||||||
 | 
									Topology: &pluginapi.TopologyInfo{
 | 
				
			||||||
 | 
										Nodes: []*pluginapi.NUMANode{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												ID: 1,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												ID: 2,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"dev3": {
 | 
				
			||||||
 | 
									ID: "dev3",
 | 
				
			||||||
 | 
									Topology: &pluginapi.TopologyInfo{
 | 
				
			||||||
 | 
										Nodes: []*pluginapi.NUMANode{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												ID: 2,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"dev4": {
 | 
				
			||||||
 | 
									ID: "dev4",
 | 
				
			||||||
 | 
									Topology: &pluginapi.TopologyInfo{
 | 
				
			||||||
 | 
										Nodes: []*pluginapi.NUMANode{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												ID: 2,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testManager := ManagerImpl{
 | 
				
			||||||
 | 
							topologyAffinityStore: newFakeTopologyManager(),
 | 
				
			||||||
 | 
							allDevices:            allDevices,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							available               sets.String
 | 
				
			||||||
 | 
							fromAffinityExpected    sets.String
 | 
				
			||||||
 | 
							notFromAffinityExpected sets.String
 | 
				
			||||||
 | 
							withoutTopologyExpected sets.String
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								available:               sets.NewString("dev1", "dev2"),
 | 
				
			||||||
 | 
								fromAffinityExpected:    sets.NewString("dev2"),
 | 
				
			||||||
 | 
								notFromAffinityExpected: sets.NewString("dev1"),
 | 
				
			||||||
 | 
								withoutTopologyExpected: sets.NewString(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								available:               sets.NewString("dev1", "dev2", "dev3", "dev4"),
 | 
				
			||||||
 | 
								fromAffinityExpected:    sets.NewString("dev2", "dev3", "dev4"),
 | 
				
			||||||
 | 
								notFromAffinityExpected: sets.NewString("dev1"),
 | 
				
			||||||
 | 
								withoutTopologyExpected: sets.NewString(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							fromAffinity, notFromAffinity, withoutTopology := testManager.filterByAffinity("", "", "res1", testCase.available)
 | 
				
			||||||
 | 
							as.Truef(fromAffinity.Equal(testCase.fromAffinityExpected), "expect devices from affinity to be %v but got %v", testCase.fromAffinityExpected, fromAffinity)
 | 
				
			||||||
 | 
							as.Truef(notFromAffinity.Equal(testCase.notFromAffinityExpected), "expect devices not from affinity to be %v but got %v", testCase.notFromAffinityExpected, notFromAffinity)
 | 
				
			||||||
 | 
							as.Truef(withoutTopology.Equal(testCase.withoutTopologyExpected), "expect devices without topology to be %v but got %v", testCase.notFromAffinityExpected, notFromAffinity)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fakeTopologyManager struct {
 | 
				
			||||||
 | 
						topologymanager.Manager
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newFakeTopologyManager() topologymanager.Manager {
 | 
				
			||||||
 | 
						return &fakeTopologyManager{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *fakeTopologyManager) GetAffinity(podUID string, containerName string) topologymanager.TopologyHint {
 | 
				
			||||||
 | 
						// return hint with numa2 set
 | 
				
			||||||
 | 
						affinity, _ := bitmask.NewBitMask(2)
 | 
				
			||||||
 | 
						return topologymanager.TopologyHint{
 | 
				
			||||||
 | 
							NUMANodeAffinity: affinity,
 | 
				
			||||||
 | 
							Preferred:        true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPodContainerDeviceAllocation(t *testing.T) {
 | 
					func TestPodContainerDeviceAllocation(t *testing.T) {
 | 
				
			||||||
	res1 := TestResource{
 | 
						res1 := TestResource{
 | 
				
			||||||
		resourceName:     "domain1.com/resource1",
 | 
							resourceName:     "domain1.com/resource1",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user