1
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"),
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