...

Source file src/k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/cm/cpumanager

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cpumanager
    18  
    19  import (
    20  	"reflect"
    21  	"sort"
    22  	"testing"
    23  
    24  	"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
    25  	"k8s.io/utils/cpuset"
    26  )
    27  
    28  func TestCPUAccumulatorFreeSockets(t *testing.T) {
    29  	testCases := []struct {
    30  		description   string
    31  		topo          *topology.CPUTopology
    32  		availableCPUs cpuset.CPUSet
    33  		expect        []int
    34  	}{
    35  		{
    36  			"single socket HT, 1 socket free",
    37  			topoSingleSocketHT,
    38  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
    39  			[]int{0},
    40  		},
    41  		{
    42  			"single socket HT, 0 sockets free",
    43  			topoSingleSocketHT,
    44  			cpuset.New(1, 2, 3, 4, 5, 6, 7),
    45  			[]int{},
    46  		},
    47  		{
    48  			"dual socket HT, 2 sockets free",
    49  			topoDualSocketHT,
    50  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
    51  			[]int{0, 1},
    52  		},
    53  		{
    54  			"dual socket HT, 1 socket free",
    55  			topoDualSocketHT,
    56  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11),
    57  			[]int{1},
    58  		},
    59  		{
    60  			"dual socket HT, 0 sockets free",
    61  			topoDualSocketHT,
    62  			cpuset.New(0, 2, 3, 4, 5, 6, 7, 8, 9, 11),
    63  			[]int{},
    64  		},
    65  		{
    66  			"dual socket, multi numa per socket, HT, 2 sockets free",
    67  			topoDualSocketMultiNumaPerSocketHT,
    68  			mustParseCPUSet(t, "0-79"),
    69  			[]int{0, 1},
    70  		},
    71  		{
    72  			"dual socket, multi numa per socket, HT, 1 sockets free",
    73  			topoDualSocketMultiNumaPerSocketHT,
    74  			mustParseCPUSet(t, "1-79"),
    75  			[]int{1},
    76  		},
    77  		{
    78  			"dual socket, multi numa per socket, HT, 0 sockets free",
    79  			topoDualSocketMultiNumaPerSocketHT,
    80  			mustParseCPUSet(t, "1-78"),
    81  			[]int{},
    82  		},
    83  		{
    84  			"dual numa, multi socket per per socket, HT, 4 sockets free",
    85  			fakeTopoMultiSocketDualSocketPerNumaHT,
    86  			mustParseCPUSet(t, "0-79"),
    87  			[]int{0, 1, 2, 3},
    88  		},
    89  		{
    90  			"dual numa, multi socket per per socket, HT, 3 sockets free",
    91  			fakeTopoMultiSocketDualSocketPerNumaHT,
    92  			mustParseCPUSet(t, "0-19,21-79"),
    93  			[]int{0, 1, 3},
    94  		},
    95  		{
    96  			"dual numa, multi socket per per socket, HT, 2 sockets free",
    97  			fakeTopoMultiSocketDualSocketPerNumaHT,
    98  			mustParseCPUSet(t, "0-59,61-78"),
    99  			[]int{0, 1},
   100  		},
   101  		{
   102  			"dual numa, multi socket per per socket, HT, 1 sockets free",
   103  			fakeTopoMultiSocketDualSocketPerNumaHT,
   104  			mustParseCPUSet(t, "1-19,21-38,41-60,61-78"),
   105  			[]int{1},
   106  		},
   107  		{
   108  			"dual numa, multi socket per per socket, HT, 0 sockets free",
   109  			fakeTopoMultiSocketDualSocketPerNumaHT,
   110  			mustParseCPUSet(t, "0-40,42-49,51-68,71-79"),
   111  			[]int{},
   112  		},
   113  	}
   114  
   115  	for _, tc := range testCases {
   116  		t.Run(tc.description, func(t *testing.T) {
   117  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
   118  			result := acc.freeSockets()
   119  			sort.Ints(result)
   120  			if !reflect.DeepEqual(result, tc.expect) {
   121  				t.Errorf("expected %v to equal %v", result, tc.expect)
   122  
   123  			}
   124  		})
   125  	}
   126  }
   127  
   128  func TestCPUAccumulatorFreeNUMANodes(t *testing.T) {
   129  	testCases := []struct {
   130  		description   string
   131  		topo          *topology.CPUTopology
   132  		availableCPUs cpuset.CPUSet
   133  		expect        []int
   134  	}{
   135  		{
   136  			"single socket HT, 1 NUMA node free",
   137  			topoSingleSocketHT,
   138  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   139  			[]int{0},
   140  		},
   141  		{
   142  			"single socket HT, 0 NUMA Node free",
   143  			topoSingleSocketHT,
   144  			cpuset.New(1, 2, 3, 4, 5, 6, 7),
   145  			[]int{},
   146  		},
   147  		{
   148  			"dual socket HT, 2 NUMA Node free",
   149  			topoDualSocketHT,
   150  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
   151  			[]int{0, 1},
   152  		},
   153  		{
   154  			"dual socket HT, 1 NUMA Node free",
   155  			topoDualSocketHT,
   156  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11),
   157  			[]int{1},
   158  		},
   159  		{
   160  			"dual socket HT, 0 NUMA node free",
   161  			topoDualSocketHT,
   162  			cpuset.New(0, 2, 3, 4, 5, 6, 7, 8, 9, 11),
   163  			[]int{},
   164  		},
   165  		{
   166  			"dual socket, multi numa per socket, HT, 4 NUMA Node free",
   167  			topoDualSocketMultiNumaPerSocketHT,
   168  			mustParseCPUSet(t, "0-79"),
   169  			[]int{0, 1, 2, 3},
   170  		},
   171  		{
   172  			"dual socket, multi numa per socket, HT, 3 NUMA node free",
   173  			topoDualSocketMultiNumaPerSocketHT,
   174  			mustParseCPUSet(t, "1-79"),
   175  			[]int{1, 2, 3},
   176  		},
   177  		{
   178  			"dual socket, multi numa per socket, HT, 2 NUMA node free",
   179  			topoDualSocketMultiNumaPerSocketHT,
   180  			mustParseCPUSet(t, "1-9,11-79"),
   181  			[]int{2, 3},
   182  		},
   183  		{
   184  			"dual socket, multi numa per socket, HT, 1 NUMA node free",
   185  			topoDualSocketMultiNumaPerSocketHT,
   186  			mustParseCPUSet(t, "1-9,11-59,61-79"),
   187  			[]int{3},
   188  		},
   189  		{
   190  			"dual socket, multi numa per socket, HT, 0 NUMA node free",
   191  			topoDualSocketMultiNumaPerSocketHT,
   192  			mustParseCPUSet(t, "1-9,11-59,61-78"),
   193  			[]int{},
   194  		},
   195  		{
   196  			"dual numa, multi socket per per socket, HT, 2 NUMA node free",
   197  			fakeTopoMultiSocketDualSocketPerNumaHT,
   198  			mustParseCPUSet(t, "0-79"),
   199  			[]int{0, 1},
   200  		},
   201  		{
   202  			"dual numa, multi socket per per socket, HT, 1 NUMA node free",
   203  			fakeTopoMultiSocketDualSocketPerNumaHT,
   204  			mustParseCPUSet(t, "0-9,11-79"),
   205  			[]int{1},
   206  		},
   207  		{
   208  			"dual numa, multi socket per per socket, HT, 0 sockets free",
   209  			fakeTopoMultiSocketDualSocketPerNumaHT,
   210  			mustParseCPUSet(t, "0-9,11-59,61-79"),
   211  			[]int{},
   212  		},
   213  	}
   214  
   215  	for _, tc := range testCases {
   216  		t.Run(tc.description, func(t *testing.T) {
   217  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
   218  			result := acc.freeNUMANodes()
   219  			if !reflect.DeepEqual(result, tc.expect) {
   220  				t.Errorf("expected %v to equal %v", result, tc.expect)
   221  			}
   222  		})
   223  	}
   224  }
   225  
   226  func TestCPUAccumulatorFreeSocketsAndNUMANodes(t *testing.T) {
   227  	testCases := []struct {
   228  		description     string
   229  		topo            *topology.CPUTopology
   230  		availableCPUs   cpuset.CPUSet
   231  		expectSockets   []int
   232  		expectNUMANodes []int
   233  	}{
   234  		{
   235  			"dual socket, multi numa per socket, HT, 2 Socket/4 NUMA Node free",
   236  			topoDualSocketMultiNumaPerSocketHT,
   237  			mustParseCPUSet(t, "0-79"),
   238  			[]int{0, 1},
   239  			[]int{0, 1, 2, 3},
   240  		},
   241  		{
   242  			"dual socket, multi numa per socket, HT, 1 Socket/3 NUMA node free",
   243  			topoDualSocketMultiNumaPerSocketHT,
   244  			mustParseCPUSet(t, "1-79"),
   245  			[]int{1},
   246  			[]int{1, 2, 3},
   247  		},
   248  		{
   249  			"dual socket, multi numa per socket, HT, 1 Socket/ 2 NUMA node free",
   250  			topoDualSocketMultiNumaPerSocketHT,
   251  			mustParseCPUSet(t, "1-9,11-79"),
   252  			[]int{1},
   253  			[]int{2, 3},
   254  		},
   255  		{
   256  			"dual socket, multi numa per socket, HT, 0 Socket/ 2 NUMA node free",
   257  			topoDualSocketMultiNumaPerSocketHT,
   258  			mustParseCPUSet(t, "1-59,61-79"),
   259  			[]int{},
   260  			[]int{1, 3},
   261  		},
   262  	}
   263  
   264  	for _, tc := range testCases {
   265  		t.Run(tc.description, func(t *testing.T) {
   266  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
   267  			resultNUMANodes := acc.freeNUMANodes()
   268  			if !reflect.DeepEqual(resultNUMANodes, tc.expectNUMANodes) {
   269  				t.Errorf("expected NUMA Nodes %v to equal %v", resultNUMANodes, tc.expectNUMANodes)
   270  			}
   271  			resultSockets := acc.freeSockets()
   272  			if !reflect.DeepEqual(resultSockets, tc.expectSockets) {
   273  				t.Errorf("expected Sockets %v to equal %v", resultSockets, tc.expectSockets)
   274  			}
   275  		})
   276  	}
   277  }
   278  
   279  func TestCPUAccumulatorFreeCores(t *testing.T) {
   280  	testCases := []struct {
   281  		description   string
   282  		topo          *topology.CPUTopology
   283  		availableCPUs cpuset.CPUSet
   284  		expect        []int
   285  	}{
   286  		{
   287  			"single socket HT, 4 cores free",
   288  			topoSingleSocketHT,
   289  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   290  			[]int{0, 1, 2, 3},
   291  		},
   292  		{
   293  			"single socket HT, 3 cores free",
   294  			topoSingleSocketHT,
   295  			cpuset.New(0, 1, 2, 4, 5, 6),
   296  			[]int{0, 1, 2},
   297  		},
   298  		{
   299  			"single socket HT, 3 cores free (1 partially consumed)",
   300  			topoSingleSocketHT,
   301  			cpuset.New(0, 1, 2, 3, 4, 5, 6),
   302  			[]int{0, 1, 2},
   303  		},
   304  		{
   305  			"single socket HT, 0 cores free",
   306  			topoSingleSocketHT,
   307  			cpuset.New(),
   308  			[]int{},
   309  		},
   310  		{
   311  			"single socket HT, 0 cores free (4 partially consumed)",
   312  			topoSingleSocketHT,
   313  			cpuset.New(0, 1, 2, 3),
   314  			[]int{},
   315  		},
   316  		{
   317  			"dual socket HT, 6 cores free",
   318  			topoDualSocketHT,
   319  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
   320  			[]int{0, 2, 4, 1, 3, 5},
   321  		},
   322  		{
   323  			"dual socket HT, 5 cores free (1 consumed from socket 0)",
   324  			topoDualSocketHT,
   325  			cpuset.New(2, 1, 3, 4, 5, 7, 8, 9, 10, 11),
   326  			[]int{2, 4, 1, 3, 5},
   327  		},
   328  		{
   329  			"dual socket HT, 4 cores free (1 consumed from each socket)",
   330  			topoDualSocketHT,
   331  			cpuset.New(2, 3, 4, 5, 8, 9, 10, 11),
   332  			[]int{2, 4, 3, 5},
   333  		},
   334  	}
   335  
   336  	for _, tc := range testCases {
   337  		t.Run(tc.description, func(t *testing.T) {
   338  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
   339  			result := acc.freeCores()
   340  			if !reflect.DeepEqual(result, tc.expect) {
   341  				t.Errorf("expected %v to equal %v", result, tc.expect)
   342  			}
   343  		})
   344  	}
   345  }
   346  
   347  func TestCPUAccumulatorFreeCPUs(t *testing.T) {
   348  	testCases := []struct {
   349  		description   string
   350  		topo          *topology.CPUTopology
   351  		availableCPUs cpuset.CPUSet
   352  		expect        []int
   353  	}{
   354  		{
   355  			"single socket HT, 8 cpus free",
   356  			topoSingleSocketHT,
   357  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   358  			[]int{0, 4, 1, 5, 2, 6, 3, 7},
   359  		},
   360  		{
   361  			"single socket HT, 5 cpus free",
   362  			topoSingleSocketHT,
   363  			cpuset.New(3, 4, 5, 6, 7),
   364  			[]int{4, 5, 6, 3, 7},
   365  		},
   366  		{
   367  			"dual socket HT, 12 cpus free",
   368  			topoDualSocketHT,
   369  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
   370  			[]int{0, 6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
   371  		},
   372  		{
   373  			"dual socket HT, 11 cpus free",
   374  			topoDualSocketHT,
   375  			cpuset.New(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
   376  			[]int{6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
   377  		},
   378  		{
   379  			"dual socket HT, 10 cpus free",
   380  			topoDualSocketHT,
   381  			cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
   382  			[]int{2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
   383  		},
   384  		{
   385  			"triple socket HT, 12 cpus free",
   386  			topoTripleSocketHT,
   387  			cpuset.New(0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13),
   388  			[]int{12, 13, 0, 1, 2, 3, 6, 7, 8, 9, 10, 11},
   389  		},
   390  	}
   391  
   392  	for _, tc := range testCases {
   393  		t.Run(tc.description, func(t *testing.T) {
   394  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
   395  			result := acc.freeCPUs()
   396  			if !reflect.DeepEqual(result, tc.expect) {
   397  				t.Errorf("expected %v to equal %v", result, tc.expect)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func TestCPUAccumulatorTake(t *testing.T) {
   404  	testCases := []struct {
   405  		description     string
   406  		topo            *topology.CPUTopology
   407  		availableCPUs   cpuset.CPUSet
   408  		takeCPUs        []cpuset.CPUSet
   409  		numCPUs         int
   410  		expectSatisfied bool
   411  		expectFailed    bool
   412  	}{
   413  		{
   414  			"take 0 cpus from a single socket HT, require 1",
   415  			topoSingleSocketHT,
   416  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   417  			[]cpuset.CPUSet{cpuset.New()},
   418  			1,
   419  			false,
   420  			false,
   421  		},
   422  		{
   423  			"take 0 cpus from a single socket HT, require 1, none available",
   424  			topoSingleSocketHT,
   425  			cpuset.New(),
   426  			[]cpuset.CPUSet{cpuset.New()},
   427  			1,
   428  			false,
   429  			true,
   430  		},
   431  		{
   432  			"take 1 cpu from a single socket HT, require 1",
   433  			topoSingleSocketHT,
   434  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   435  			[]cpuset.CPUSet{cpuset.New(0)},
   436  			1,
   437  			true,
   438  			false,
   439  		},
   440  		{
   441  			"take 1 cpu from a single socket HT, require 2",
   442  			topoSingleSocketHT,
   443  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   444  			[]cpuset.CPUSet{cpuset.New(0)},
   445  			2,
   446  			false,
   447  			false,
   448  		},
   449  		{
   450  			"take 2 cpu from a single socket HT, require 4, expect failed",
   451  			topoSingleSocketHT,
   452  			cpuset.New(0, 1, 2),
   453  			[]cpuset.CPUSet{cpuset.New(0), cpuset.New(1)},
   454  			4,
   455  			false,
   456  			true,
   457  		},
   458  		{
   459  			"take all cpus one at a time from a single socket HT, require 8",
   460  			topoSingleSocketHT,
   461  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   462  			[]cpuset.CPUSet{
   463  				cpuset.New(0),
   464  				cpuset.New(1),
   465  				cpuset.New(2),
   466  				cpuset.New(3),
   467  				cpuset.New(4),
   468  				cpuset.New(5),
   469  				cpuset.New(6),
   470  				cpuset.New(7),
   471  			},
   472  			8,
   473  			true,
   474  			false,
   475  		},
   476  	}
   477  
   478  	for _, tc := range testCases {
   479  		t.Run(tc.description, func(t *testing.T) {
   480  			acc := newCPUAccumulator(tc.topo, tc.availableCPUs, tc.numCPUs)
   481  			totalTaken := 0
   482  			for _, cpus := range tc.takeCPUs {
   483  				acc.take(cpus)
   484  				totalTaken += cpus.Size()
   485  			}
   486  			if tc.expectSatisfied != acc.isSatisfied() {
   487  				t.Errorf("expected acc.isSatisfied() to be %t", tc.expectSatisfied)
   488  			}
   489  			if tc.expectFailed != acc.isFailed() {
   490  				t.Errorf("expected acc.isFailed() to be %t", tc.expectFailed)
   491  			}
   492  			for _, cpus := range tc.takeCPUs {
   493  				availableCPUs := acc.details.CPUs()
   494  				if cpus.Intersection(availableCPUs).Size() > 0 {
   495  					t.Errorf("expected intersection of taken cpus [%s] and acc.details.CPUs() [%s] to be empty", cpus, availableCPUs)
   496  				}
   497  				if !cpus.IsSubsetOf(acc.result) {
   498  					t.Errorf("expected [%s] to be a subset of acc.result [%s]", cpus, acc.result)
   499  				}
   500  			}
   501  			expNumCPUsNeeded := tc.numCPUs - totalTaken
   502  			if acc.numCPUsNeeded != expNumCPUsNeeded {
   503  				t.Errorf("expected acc.numCPUsNeeded to be %d (got %d)", expNumCPUsNeeded, acc.numCPUsNeeded)
   504  			}
   505  		})
   506  	}
   507  }
   508  
   509  type takeByTopologyTestCase struct {
   510  	description   string
   511  	topo          *topology.CPUTopology
   512  	availableCPUs cpuset.CPUSet
   513  	numCPUs       int
   514  	expErr        string
   515  	expResult     cpuset.CPUSet
   516  }
   517  
   518  func commonTakeByTopologyTestCases(t *testing.T) []takeByTopologyTestCase {
   519  	return []takeByTopologyTestCase{
   520  		{
   521  			"take more cpus than are available from single socket with HT",
   522  			topoSingleSocketHT,
   523  			cpuset.New(0, 2, 4, 6),
   524  			5,
   525  			"not enough cpus available to satisfy request: requested=5, available=4",
   526  			cpuset.New(),
   527  		},
   528  		{
   529  			"take zero cpus from single socket with HT",
   530  			topoSingleSocketHT,
   531  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   532  			0,
   533  			"",
   534  			cpuset.New(),
   535  		},
   536  		{
   537  			"take one cpu from single socket with HT",
   538  			topoSingleSocketHT,
   539  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   540  			1,
   541  			"",
   542  			cpuset.New(0),
   543  		},
   544  		{
   545  			"take one cpu from single socket with HT, some cpus are taken",
   546  			topoSingleSocketHT,
   547  			cpuset.New(1, 3, 5, 6, 7),
   548  			1,
   549  			"",
   550  			cpuset.New(6),
   551  		},
   552  		{
   553  			"take two cpus from single socket with HT",
   554  			topoSingleSocketHT,
   555  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   556  			2,
   557  			"",
   558  			cpuset.New(0, 4),
   559  		},
   560  		{
   561  			"take all cpus from single socket with HT",
   562  			topoSingleSocketHT,
   563  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   564  			8,
   565  			"",
   566  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7),
   567  		},
   568  		{
   569  			"take two cpus from single socket with HT, only one core totally free",
   570  			topoSingleSocketHT,
   571  			cpuset.New(0, 1, 2, 3, 6),
   572  			2,
   573  			"",
   574  			cpuset.New(2, 6),
   575  		},
   576  		{
   577  			"take a socket of cpus from dual socket with HT",
   578  			topoDualSocketHT,
   579  			cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
   580  			6,
   581  			"",
   582  			cpuset.New(0, 2, 4, 6, 8, 10),
   583  		},
   584  		{
   585  			"take a socket of cpus from dual socket with multi-numa-per-socket with HT",
   586  			topoDualSocketMultiNumaPerSocketHT,
   587  			mustParseCPUSet(t, "0-79"),
   588  			40,
   589  			"",
   590  			mustParseCPUSet(t, "0-19,40-59"),
   591  		},
   592  		{
   593  			"take a NUMA node of cpus from dual socket with multi-numa-per-socket with HT",
   594  			topoDualSocketMultiNumaPerSocketHT,
   595  			mustParseCPUSet(t, "0-79"),
   596  			20,
   597  			"",
   598  			mustParseCPUSet(t, "0-9,40-49"),
   599  		},
   600  		{
   601  			"take a NUMA node of cpus from dual socket with multi-numa-per-socket with HT, with 1 NUMA node already taken",
   602  			topoDualSocketMultiNumaPerSocketHT,
   603  			mustParseCPUSet(t, "10-39,50-79"),
   604  			20,
   605  			"",
   606  			mustParseCPUSet(t, "10-19,50-59"),
   607  		},
   608  		{
   609  			"take a socket and a NUMA node of cpus from dual socket with multi-numa-per-socket with HT",
   610  			topoDualSocketMultiNumaPerSocketHT,
   611  			mustParseCPUSet(t, "0-79"),
   612  			60,
   613  			"",
   614  			mustParseCPUSet(t, "0-29,40-69"),
   615  		},
   616  		{
   617  			"take a socket and a NUMA node of cpus from dual socket with multi-numa-per-socket with HT, a core taken",
   618  			topoDualSocketMultiNumaPerSocketHT,
   619  			mustParseCPUSet(t, "1-39,41-79"), // reserve the first (phys) core (0,40)
   620  			60,
   621  			"",
   622  			mustParseCPUSet(t, "10-39,50-79"),
   623  		},
   624  	}
   625  }
   626  
   627  func TestTakeByTopologyNUMAPacked(t *testing.T) {
   628  	testCases := commonTakeByTopologyTestCases(t)
   629  	testCases = append(testCases, []takeByTopologyTestCase{
   630  		{
   631  			"take one cpu from dual socket with HT - core from Socket 0",
   632  			topoDualSocketHT,
   633  			cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
   634  			1,
   635  			"",
   636  			cpuset.New(2),
   637  		},
   638  		{
   639  			"allocate 4 full cores with 3 coming from the first NUMA node (filling it up) and 1 coming from the second NUMA node",
   640  			topoDualSocketHT,
   641  			mustParseCPUSet(t, "0-11"),
   642  			8,
   643  			"",
   644  			mustParseCPUSet(t, "0,6,2,8,4,10,1,7"),
   645  		},
   646  		{
   647  			"allocate 32 full cores with 30 coming from the first 3 NUMA nodes (filling them up) and 2 coming from the fourth NUMA node",
   648  			topoDualSocketMultiNumaPerSocketHT,
   649  			mustParseCPUSet(t, "0-79"),
   650  			64,
   651  			"",
   652  			mustParseCPUSet(t, "0-29,40-69,30,31,70,71"),
   653  		},
   654  	}...)
   655  
   656  	for _, tc := range testCases {
   657  		t.Run(tc.description, func(t *testing.T) {
   658  			result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs)
   659  			if tc.expErr != "" && err != nil && err.Error() != tc.expErr {
   660  				t.Errorf("expected error to be [%v] but it was [%v]", tc.expErr, err)
   661  			}
   662  			if !result.Equals(tc.expResult) {
   663  				t.Errorf("expected result [%s] to equal [%s]", result, tc.expResult)
   664  			}
   665  		})
   666  	}
   667  }
   668  
   669  type takeByTopologyExtendedTestCase struct {
   670  	description   string
   671  	topo          *topology.CPUTopology
   672  	availableCPUs cpuset.CPUSet
   673  	numCPUs       int
   674  	cpuGroupSize  int
   675  	expErr        string
   676  	expResult     cpuset.CPUSet
   677  }
   678  
   679  func commonTakeByTopologyExtendedTestCases(t *testing.T) []takeByTopologyExtendedTestCase {
   680  	var extendedTestCases []takeByTopologyExtendedTestCase
   681  
   682  	testCases := commonTakeByTopologyTestCases(t)
   683  	for _, tc := range testCases {
   684  		extendedTestCases = append(extendedTestCases, takeByTopologyExtendedTestCase{
   685  			tc.description,
   686  			tc.topo,
   687  			tc.availableCPUs,
   688  			tc.numCPUs,
   689  			1,
   690  			tc.expErr,
   691  			tc.expResult,
   692  		})
   693  	}
   694  
   695  	extendedTestCases = append(extendedTestCases, []takeByTopologyExtendedTestCase{
   696  		{
   697  			"allocate 4 full cores with 2 distributed across each NUMA node",
   698  			topoDualSocketHT,
   699  			mustParseCPUSet(t, "0-11"),
   700  			8,
   701  			1,
   702  			"",
   703  			mustParseCPUSet(t, "0,6,2,8,1,7,3,9"),
   704  		},
   705  		{
   706  			"allocate 32 full cores with 8 distributed across each NUMA node",
   707  			topoDualSocketMultiNumaPerSocketHT,
   708  			mustParseCPUSet(t, "0-79"),
   709  			64,
   710  			1,
   711  			"",
   712  			mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-47,50-57,60-67,70-77"),
   713  		},
   714  		{
   715  			"allocate 24 full cores with 8 distributed across the first 3 NUMA nodes",
   716  			topoDualSocketMultiNumaPerSocketHT,
   717  			mustParseCPUSet(t, "0-79"),
   718  			48,
   719  			1,
   720  			"",
   721  			mustParseCPUSet(t, "0-7,10-17,20-27,40-47,50-57,60-67"),
   722  		},
   723  		{
   724  			"allocate 24 full cores with 8 distributed across the first 3 NUMA nodes (taking all but 2 from the first NUMA node)",
   725  			topoDualSocketMultiNumaPerSocketHT,
   726  			mustParseCPUSet(t, "1-29,32-39,41-69,72-79"),
   727  			48,
   728  			1,
   729  			"",
   730  			mustParseCPUSet(t, "1-8,10-17,20-27,41-48,50-57,60-67"),
   731  		},
   732  		{
   733  			"allocate 24 full cores with 8 distributed across the last 3 NUMA nodes (even though all 8 could be allocated from the first NUMA node)",
   734  			topoDualSocketMultiNumaPerSocketHT,
   735  			mustParseCPUSet(t, "2-29,31-39,42-69,71-79"),
   736  			48,
   737  			1,
   738  			"",
   739  			mustParseCPUSet(t, "10-17,20-27,31-38,50-57,60-67,71-78"),
   740  		},
   741  		{
   742  			"allocate 8 full cores with 2 distributed across each NUMA node",
   743  			topoDualSocketMultiNumaPerSocketHT,
   744  			mustParseCPUSet(t, "0-2,10-12,20-22,30-32,40-41,50-51,60-61,70-71"),
   745  			16,
   746  			1,
   747  			"",
   748  			mustParseCPUSet(t, "0-1,10-11,20-21,30-31,40-41,50-51,60-61,70-71"),
   749  		},
   750  		{
   751  			"allocate 8 full cores with 2 distributed across each NUMA node",
   752  			topoDualSocketMultiNumaPerSocketHT,
   753  			mustParseCPUSet(t, "0-2,10-12,20-22,30-32,40-41,50-51,60-61,70-71"),
   754  			16,
   755  			1,
   756  			"",
   757  			mustParseCPUSet(t, "0-1,10-11,20-21,30-31,40-41,50-51,60-61,70-71"),
   758  		},
   759  	}...)
   760  
   761  	return extendedTestCases
   762  }
   763  
   764  func TestTakeByTopologyNUMADistributed(t *testing.T) {
   765  	testCases := commonTakeByTopologyExtendedTestCases(t)
   766  	testCases = append(testCases, []takeByTopologyExtendedTestCase{
   767  		{
   768  			"take one cpu from dual socket with HT - core from Socket 0",
   769  			topoDualSocketHT,
   770  			cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
   771  			1,
   772  			1,
   773  			"",
   774  			cpuset.New(1),
   775  		},
   776  		{
   777  			"take one cpu from dual socket with HT - core from Socket 0 - cpuGroupSize 2",
   778  			topoDualSocketHT,
   779  			cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
   780  			1,
   781  			2,
   782  			"",
   783  			cpuset.New(2),
   784  		},
   785  		{
   786  			"allocate 13 full cores distributed across the first 2 NUMA nodes",
   787  			topoDualSocketMultiNumaPerSocketHT,
   788  			mustParseCPUSet(t, "0-79"),
   789  			26,
   790  			1,
   791  			"",
   792  			mustParseCPUSet(t, "0-6,10-16,40-45,50-55"),
   793  		},
   794  		{
   795  			"allocate 13 full cores distributed across the first 2 NUMA nodes (cpuGroupSize 2)",
   796  			topoDualSocketMultiNumaPerSocketHT,
   797  			mustParseCPUSet(t, "0-79"),
   798  			26,
   799  			2,
   800  			"",
   801  			mustParseCPUSet(t, "0-6,10-15,40-46,50-55"),
   802  		},
   803  		{
   804  			"allocate 31 full cores with 15 CPUs distributed across each NUMA node and 1 CPU spilling over to each of NUMA 0, 1",
   805  			topoDualSocketMultiNumaPerSocketHT,
   806  			mustParseCPUSet(t, "0-79"),
   807  			62,
   808  			1,
   809  			"",
   810  			mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-47,50-57,60-66,70-76"),
   811  		},
   812  		{
   813  			"allocate 31 full cores with 14 CPUs distributed across each NUMA node and 2 CPUs spilling over to each of NUMA 0, 1, 2 (cpuGroupSize 2)",
   814  			topoDualSocketMultiNumaPerSocketHT,
   815  			mustParseCPUSet(t, "0-79"),
   816  			62,
   817  			2,
   818  			"",
   819  			mustParseCPUSet(t, "0-7,10-17,20-27,30-36,40-47,50-57,60-67,70-76"),
   820  		},
   821  		{
   822  			"allocate 31 full cores with 15 CPUs distributed across each NUMA node and 1 CPU spilling over to each of NUMA 2, 3 (to keep balance)",
   823  			topoDualSocketMultiNumaPerSocketHT,
   824  			mustParseCPUSet(t, "0-8,10-18,20-39,40-48,50-58,60-79"),
   825  			62,
   826  			1,
   827  			"",
   828  			mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-46,50-56,60-67,70-77"),
   829  		},
   830  		{
   831  			"allocate 31 full cores with 14 CPUs distributed across each NUMA node and 2 CPUs spilling over to each of NUMA 0, 2, 3 (to keep balance with cpuGroupSize 2)",
   832  			topoDualSocketMultiNumaPerSocketHT,
   833  			mustParseCPUSet(t, "0-8,10-18,20-39,40-48,50-58,60-79"),
   834  			62,
   835  			2,
   836  			"",
   837  			mustParseCPUSet(t, "0-7,10-16,20-27,30-37,40-47,50-56,60-67,70-77"),
   838  		},
   839  		{
   840  			"ensure bestRemainder chosen with NUMA nodes that have enough CPUs to satisfy the request",
   841  			topoDualSocketMultiNumaPerSocketHT,
   842  			mustParseCPUSet(t, "0-3,10-13,20-23,30-36,40-43,50-53,60-63,70-76"),
   843  			34,
   844  			1,
   845  			"",
   846  			mustParseCPUSet(t, "0-3,10-13,20-23,30-34,40-43,50-53,60-63,70-74"),
   847  		},
   848  		{
   849  			"ensure previous failure encountered on live machine has been fixed (1/1)",
   850  			topoDualSocketMultiNumaPerSocketHTLarge,
   851  			mustParseCPUSet(t, "0,128,30,31,158,159,43-47,171-175,62,63,190,191,75-79,203-207,94,96,222,223,101-111,229-239,126,127,254,255"),
   852  			28,
   853  			1,
   854  			"",
   855  			mustParseCPUSet(t, "43-47,75-79,96,101-105,171-174,203-206,229-232"),
   856  		},
   857  	}...)
   858  
   859  	for _, tc := range testCases {
   860  		t.Run(tc.description, func(t *testing.T) {
   861  			result, err := takeByTopologyNUMADistributed(tc.topo, tc.availableCPUs, tc.numCPUs, tc.cpuGroupSize)
   862  			if err != nil {
   863  				if tc.expErr == "" {
   864  					t.Errorf("unexpected error [%v]", err)
   865  				}
   866  				if tc.expErr != "" && err.Error() != tc.expErr {
   867  					t.Errorf("expected error to be [%v] but it was [%v]", tc.expErr, err)
   868  				}
   869  				return
   870  			}
   871  			if !result.Equals(tc.expResult) {
   872  				t.Errorf("expected result [%s] to equal [%s]", result, tc.expResult)
   873  			}
   874  		})
   875  	}
   876  }
   877  
   878  func mustParseCPUSet(t *testing.T, s string) cpuset.CPUSet {
   879  	cpus, err := cpuset.Parse(s)
   880  	if err != nil {
   881  		t.Errorf("parsing %q: %v", s, err)
   882  	}
   883  	return cpus
   884  }
   885  

View as plain text