github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/process_linux_test.go (about) 1 package libcontainer 2 3 import ( 4 "io/fs" 5 "testing" 6 "testing/fstest" 7 ) 8 9 func TestIsolatedCPUAffinityTransition(t *testing.T) { 10 const isolatedCPUAffinityTransitionAnnotation = "org.opencontainers.runc.exec.isolated-cpu-affinity-transition" 11 12 noAffinity := -1 13 temporaryTransition := "temporary" 14 definitiveTransition := "definitive" 15 16 tests := []struct { 17 name string 18 testFS fs.FS 19 cpuset string 20 expectedErr bool 21 expectedAffinityCore int 22 expectedDefinitiveTransition bool 23 annotations map[string]string 24 }{ 25 { 26 name: "no affinity", 27 cpuset: "0-15", 28 testFS: fstest.MapFS{ 29 "sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")}, 30 }, 31 expectedAffinityCore: noAffinity, 32 expectedDefinitiveTransition: false, 33 }, 34 { 35 name: "affinity match with temporary transition", 36 cpuset: "3-4", 37 testFS: fstest.MapFS{ 38 "sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")}, 39 }, 40 expectedAffinityCore: 3, 41 expectedDefinitiveTransition: false, 42 annotations: map[string]string{ 43 isolatedCPUAffinityTransitionAnnotation: temporaryTransition, 44 }, 45 }, 46 { 47 name: "affinity match with temporary transition and nohz_full boot param", 48 cpuset: "3-4", 49 testFS: fstest.MapFS{ 50 "proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=0-4\n")}, 51 }, 52 expectedAffinityCore: 3, 53 expectedDefinitiveTransition: false, 54 annotations: map[string]string{ 55 isolatedCPUAffinityTransitionAnnotation: temporaryTransition, 56 }, 57 }, 58 { 59 name: "affinity match with definitive transition", 60 cpuset: "3-4", 61 testFS: fstest.MapFS{ 62 "sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")}, 63 }, 64 expectedAffinityCore: 3, 65 expectedDefinitiveTransition: true, 66 annotations: map[string]string{ 67 isolatedCPUAffinityTransitionAnnotation: definitiveTransition, 68 }, 69 }, 70 { 71 name: "affinity match with definitive transition and nohz_full boot param", 72 cpuset: "3-4", 73 testFS: fstest.MapFS{ 74 "proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=0-4\n")}, 75 }, 76 expectedAffinityCore: 3, 77 expectedDefinitiveTransition: true, 78 annotations: map[string]string{ 79 isolatedCPUAffinityTransitionAnnotation: definitiveTransition, 80 }, 81 }, 82 { 83 name: "affinity error with bad isolated set", 84 cpuset: "0-15", 85 testFS: fstest.MapFS{ 86 "sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("bad_isolated_set\n")}, 87 }, 88 expectedErr: true, 89 expectedAffinityCore: noAffinity, 90 annotations: map[string]string{ 91 isolatedCPUAffinityTransitionAnnotation: temporaryTransition, 92 }, 93 }, 94 { 95 name: "affinity error with bad isolated set for nohz_full boot param", 96 cpuset: "0-15", 97 testFS: fstest.MapFS{ 98 "proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=bad_isolated_set\n")}, 99 }, 100 expectedErr: true, 101 expectedAffinityCore: noAffinity, 102 annotations: map[string]string{ 103 isolatedCPUAffinityTransitionAnnotation: temporaryTransition, 104 }, 105 }, 106 { 107 name: "no affinity with null isolated set value", 108 cpuset: "0-15", 109 testFS: fstest.MapFS{ 110 "sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("(null)\n")}, 111 }, 112 expectedAffinityCore: noAffinity, 113 expectedDefinitiveTransition: false, 114 annotations: map[string]string{ 115 isolatedCPUAffinityTransitionAnnotation: temporaryTransition, 116 }, 117 }, 118 } 119 120 for _, tt := range tests { 121 t.Run(tt.name, func(t *testing.T) { 122 affinityCore, definitive, err := isolatedCPUAffinityTransition(tt.testFS, tt.cpuset, tt.annotations) 123 if err != nil && !tt.expectedErr { 124 t.Fatalf("unexpected error: %s", err) 125 } else if err == nil && tt.expectedErr { 126 t.Fatalf("unexpected success") 127 } else if tt.expectedDefinitiveTransition != definitive { 128 t.Fatalf("expected reset affinity %t: got %t instead", tt.expectedDefinitiveTransition, definitive) 129 } else if tt.expectedAffinityCore != affinityCore { 130 t.Fatalf("expected affinity core %d: got %d instead", tt.expectedAffinityCore, affinityCore) 131 } 132 }) 133 } 134 } 135 136 func TestGetEligibleCPU(t *testing.T) { 137 tests := []struct { 138 name string 139 cpuset string 140 isolset string 141 expectedErr bool 142 expectedAffinityCore int 143 expectedEligible bool 144 }{ 145 { 146 name: "no cpuset", 147 isolset: "2-15,18-31,34-47", 148 expectedEligible: false, 149 }, 150 { 151 name: "no isolated set", 152 cpuset: "0-15", 153 expectedEligible: false, 154 }, 155 { 156 name: "bad cpuset format", 157 cpuset: "core0 to core15", 158 isolset: "2-15,18-31,34-47", 159 expectedErr: true, 160 }, 161 { 162 name: "bad isolated set format", 163 cpuset: "0-15", 164 isolset: "core0 to core15", 165 expectedErr: true, 166 }, 167 { 168 name: "no eligible core", 169 cpuset: "0-1,16-17,32-33", 170 isolset: "2-15,18-31,34-47", 171 expectedEligible: false, 172 }, 173 { 174 name: "no eligible core inverted", 175 cpuset: "2-15,18-31,34-47", 176 isolset: "0-1,16-17,32-33", 177 expectedEligible: false, 178 }, 179 { 180 name: "eligible core mixed", 181 cpuset: "8-31", 182 isolset: "2-15,18-31,34-47", 183 expectedEligible: true, 184 expectedAffinityCore: 16, 185 }, 186 { 187 name: "eligible core #4", 188 cpuset: "4-7", 189 isolset: "2-15,18-31,34-47", 190 expectedEligible: true, 191 expectedAffinityCore: 4, 192 }, 193 { 194 name: "eligible core #40", 195 cpuset: "40-47", 196 isolset: "2-15,18-31,34-47", 197 expectedEligible: true, 198 expectedAffinityCore: 40, 199 }, 200 { 201 name: "eligible core #24", 202 cpuset: "24-31", 203 isolset: "2-15,18-31,34-47", 204 expectedEligible: true, 205 expectedAffinityCore: 24, 206 }, 207 { 208 name: "no eligible core small isolated set", 209 cpuset: "60-63", 210 isolset: "0-1", 211 expectedEligible: false, 212 }, 213 } 214 215 for _, tt := range tests { 216 t.Run(tt.name, func(t *testing.T) { 217 affinityCore, err := getEligibleCPU(tt.cpuset, tt.isolset) 218 eligible := affinityCore >= 0 219 if err != nil && !tt.expectedErr { 220 t.Fatalf("unexpected error: %s", err) 221 } else if err == nil && tt.expectedErr { 222 t.Fatalf("unexpected success") 223 } else if tt.expectedEligible && !eligible { 224 t.Fatalf("was expecting eligible core but no eligible core returned") 225 } else if !tt.expectedEligible && eligible { 226 t.Fatalf("was not expecting eligible core but got eligible core") 227 } else if tt.expectedEligible && tt.expectedAffinityCore != affinityCore { 228 t.Fatalf("expected affinity core %d: got %d instead", tt.expectedAffinityCore, affinityCore) 229 } 230 }) 231 } 232 }