topo_linux_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //go:build linux
  2. package cpupick
  3. import (
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "testing"
  8. )
  9. // fakeTopoSysfs builds nodeDir/cpuDir trees. nodes maps node id -> cpulist
  10. // string; cores maps cpu -> (package, core) pair.
  11. func fakeTopoSysfs(t *testing.T, nodes map[int]string, cores map[int][2]int) (string, string) {
  12. t.Helper()
  13. base := t.TempDir()
  14. nodeDir := filepath.Join(base, "node")
  15. cpuDir := filepath.Join(base, "cpu")
  16. for n, list := range nodes {
  17. d := filepath.Join(nodeDir, fmt.Sprintf("node%d", n))
  18. if err := os.MkdirAll(d, 0o755); err != nil {
  19. t.Fatal(err)
  20. }
  21. if err := os.WriteFile(filepath.Join(d, "cpulist"), []byte(list+"\n"), 0o644); err != nil {
  22. t.Fatal(err)
  23. }
  24. }
  25. for cpu, pc := range cores {
  26. d := filepath.Join(cpuDir, fmt.Sprintf("cpu%d", cpu), "topology")
  27. if err := os.MkdirAll(d, 0o755); err != nil {
  28. t.Fatal(err)
  29. }
  30. if err := os.WriteFile(filepath.Join(d, "physical_package_id"), fmt.Appendf(nil, "%d\n", pc[0]), 0o644); err != nil {
  31. t.Fatal(err)
  32. }
  33. if err := os.WriteFile(filepath.Join(d, "core_id"), fmt.Appendf(nil, "%d\n", pc[1]), 0o644); err != nil {
  34. t.Fatal(err)
  35. }
  36. }
  37. return nodeDir, cpuDir
  38. }
  39. func TestReadTopology(t *testing.T) {
  40. // Two nodes; SMT pairs (0,4),(1,5) on node 0 and (2,6),(3,7) on node 1.
  41. // core_id repeats across packages on purpose: the pair must disambiguate.
  42. nodeDir, cpuDir := fakeTopoSysfs(t,
  43. map[int]string{0: "0-1,4-5", 1: "2-3,6-7"},
  44. map[int][2]int{
  45. 0: {0, 0}, 4: {0, 0}, 1: {0, 1}, 5: {0, 1},
  46. 2: {1, 0}, 6: {1, 0}, 3: {1, 1}, 7: {1, 1},
  47. })
  48. cpus := []int{0, 1, 2, 3, 4, 5, 6, 7}
  49. topo := readTopologyFrom(nodeDir, cpuDir, cpus)
  50. for _, c := range []int{0, 1, 4, 5} {
  51. if topo.nodeOf[c] != 0 {
  52. t.Errorf("cpu %d on node %d, want 0", c, topo.nodeOf[c])
  53. }
  54. }
  55. for _, c := range []int{2, 3, 6, 7} {
  56. if topo.nodeOf[c] != 1 {
  57. t.Errorf("cpu %d on node %d, want 1", c, topo.nodeOf[c])
  58. }
  59. }
  60. pairs := [][2]int{{0, 4}, {1, 5}, {2, 6}, {3, 7}}
  61. for _, p := range pairs {
  62. if topo.coreOf[p[0]] != topo.coreOf[p[1]] {
  63. t.Errorf("siblings %v not grouped: %d vs %d", p, topo.coreOf[p[0]], topo.coreOf[p[1]])
  64. }
  65. }
  66. if topo.coreOf[0] == topo.coreOf[2] {
  67. t.Error("cross-package cores with equal core_id must not merge")
  68. }
  69. if topo.zeroCore != topo.coreOf[0] {
  70. t.Errorf("zeroCore = %d, want %d", topo.zeroCore, topo.coreOf[0])
  71. }
  72. }
  73. func TestReadTopologyZeroCoreWithoutZeroCandidate(t *testing.T) {
  74. // CPU 0 is not a candidate (cpuset excludes it) but its sibling 4 is:
  75. // zeroCore must still identify their shared core.
  76. nodeDir, cpuDir := fakeTopoSysfs(t,
  77. map[int]string{0: "0-7"},
  78. map[int][2]int{0: {0, 0}, 4: {0, 0}, 1: {0, 1}, 5: {0, 1}})
  79. topo := readTopologyFrom(nodeDir, cpuDir, []int{1, 4, 5})
  80. if topo.zeroCore < 0 || topo.coreOf[4] != topo.zeroCore {
  81. t.Errorf("zeroCore = %d, coreOf[4] = %d; sibling of CPU 0 not identified", topo.zeroCore, topo.coreOf[4])
  82. }
  83. if topo.coreOf[1] == topo.zeroCore {
  84. t.Error("cpu 1 wrongly grouped with CPU 0's core")
  85. }
  86. }
  87. func TestReadTopologyMissingSysfs(t *testing.T) {
  88. base := t.TempDir()
  89. cpus := []int{0, 1, 2}
  90. topo := readTopologyFrom(filepath.Join(base, "nope"), filepath.Join(base, "also-nope"), cpus)
  91. seen := map[int]bool{}
  92. for _, c := range cpus {
  93. if topo.nodeOf[c] != 0 {
  94. t.Errorf("cpu %d node = %d, want 0", c, topo.nodeOf[c])
  95. }
  96. if seen[topo.coreOf[c]] {
  97. t.Errorf("cpu %d shares a fallback core group", c)
  98. }
  99. seen[topo.coreOf[c]] = true
  100. }
  101. if topo.zeroCore != -1 {
  102. t.Errorf("zeroCore = %d, want -1 when unknown", topo.zeroCore)
  103. }
  104. }