k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/cpumanager/topology/topology.go (about) 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 topology 18 19 import ( 20 "fmt" 21 22 cadvisorapi "github.com/google/cadvisor/info/v1" 23 "k8s.io/klog/v2" 24 "k8s.io/utils/cpuset" 25 ) 26 27 // NUMANodeInfo is a map from NUMANode ID to a list of CPU IDs associated with 28 // that NUMANode. 29 type NUMANodeInfo map[int]cpuset.CPUSet 30 31 // CPUDetails is a map from CPU ID to Core ID, Socket ID, and NUMA ID. 32 type CPUDetails map[int]CPUInfo 33 34 // CPUTopology contains details of node cpu, where : 35 // CPU - logical CPU, cadvisor - thread 36 // Core - physical CPU, cadvisor - Core 37 // Socket - socket, cadvisor - Socket 38 // NUMA Node - NUMA cell, cadvisor - Node 39 type CPUTopology struct { 40 NumCPUs int 41 NumCores int 42 NumSockets int 43 NumNUMANodes int 44 CPUDetails CPUDetails 45 } 46 47 // CPUsPerCore returns the number of logical CPUs are associated with 48 // each core. 49 func (topo *CPUTopology) CPUsPerCore() int { 50 if topo.NumCores == 0 { 51 return 0 52 } 53 return topo.NumCPUs / topo.NumCores 54 } 55 56 // CPUsPerSocket returns the number of logical CPUs are associated with 57 // each socket. 58 func (topo *CPUTopology) CPUsPerSocket() int { 59 if topo.NumSockets == 0 { 60 return 0 61 } 62 return topo.NumCPUs / topo.NumSockets 63 } 64 65 // CPUCoreID returns the physical core ID which the given logical CPU 66 // belongs to. 67 func (topo *CPUTopology) CPUCoreID(cpu int) (int, error) { 68 info, ok := topo.CPUDetails[cpu] 69 if !ok { 70 return -1, fmt.Errorf("unknown CPU ID: %d", cpu) 71 } 72 return info.CoreID, nil 73 } 74 75 // CPUCoreID returns the socket ID which the given logical CPU belongs to. 76 func (topo *CPUTopology) CPUSocketID(cpu int) (int, error) { 77 info, ok := topo.CPUDetails[cpu] 78 if !ok { 79 return -1, fmt.Errorf("unknown CPU ID: %d", cpu) 80 } 81 return info.SocketID, nil 82 } 83 84 // CPUCoreID returns the NUMA node ID which the given logical CPU belongs to. 85 func (topo *CPUTopology) CPUNUMANodeID(cpu int) (int, error) { 86 info, ok := topo.CPUDetails[cpu] 87 if !ok { 88 return -1, fmt.Errorf("unknown CPU ID: %d", cpu) 89 } 90 return info.NUMANodeID, nil 91 } 92 93 // CPUInfo contains the NUMA, socket, and core IDs associated with a CPU. 94 type CPUInfo struct { 95 NUMANodeID int 96 SocketID int 97 CoreID int 98 } 99 100 // KeepOnly returns a new CPUDetails object with only the supplied cpus. 101 func (d CPUDetails) KeepOnly(cpus cpuset.CPUSet) CPUDetails { 102 result := CPUDetails{} 103 for cpu, info := range d { 104 if cpus.Contains(cpu) { 105 result[cpu] = info 106 } 107 } 108 return result 109 } 110 111 // NUMANodes returns all of the NUMANode IDs associated with the CPUs in this 112 // CPUDetails. 113 func (d CPUDetails) NUMANodes() cpuset.CPUSet { 114 var numaNodeIDs []int 115 for _, info := range d { 116 numaNodeIDs = append(numaNodeIDs, info.NUMANodeID) 117 } 118 return cpuset.New(numaNodeIDs...) 119 } 120 121 // NUMANodesInSockets returns all of the logical NUMANode IDs associated with 122 // the given socket IDs in this CPUDetails. 123 func (d CPUDetails) NUMANodesInSockets(ids ...int) cpuset.CPUSet { 124 var numaNodeIDs []int 125 for _, id := range ids { 126 for _, info := range d { 127 if info.SocketID == id { 128 numaNodeIDs = append(numaNodeIDs, info.NUMANodeID) 129 } 130 } 131 } 132 return cpuset.New(numaNodeIDs...) 133 } 134 135 // Sockets returns all of the socket IDs associated with the CPUs in this 136 // CPUDetails. 137 func (d CPUDetails) Sockets() cpuset.CPUSet { 138 var socketIDs []int 139 for _, info := range d { 140 socketIDs = append(socketIDs, info.SocketID) 141 } 142 return cpuset.New(socketIDs...) 143 } 144 145 // CPUsInSockets returns all of the logical CPU IDs associated with the given 146 // socket IDs in this CPUDetails. 147 func (d CPUDetails) CPUsInSockets(ids ...int) cpuset.CPUSet { 148 var cpuIDs []int 149 for _, id := range ids { 150 for cpu, info := range d { 151 if info.SocketID == id { 152 cpuIDs = append(cpuIDs, cpu) 153 } 154 } 155 } 156 return cpuset.New(cpuIDs...) 157 } 158 159 // SocketsInNUMANodes returns all of the logical Socket IDs associated with the 160 // given NUMANode IDs in this CPUDetails. 161 func (d CPUDetails) SocketsInNUMANodes(ids ...int) cpuset.CPUSet { 162 var socketIDs []int 163 for _, id := range ids { 164 for _, info := range d { 165 if info.NUMANodeID == id { 166 socketIDs = append(socketIDs, info.SocketID) 167 } 168 } 169 } 170 return cpuset.New(socketIDs...) 171 } 172 173 // Cores returns all of the core IDs associated with the CPUs in this 174 // CPUDetails. 175 func (d CPUDetails) Cores() cpuset.CPUSet { 176 var coreIDs []int 177 for _, info := range d { 178 coreIDs = append(coreIDs, info.CoreID) 179 } 180 return cpuset.New(coreIDs...) 181 } 182 183 // CoresInNUMANodes returns all of the core IDs associated with the given 184 // NUMANode IDs in this CPUDetails. 185 func (d CPUDetails) CoresInNUMANodes(ids ...int) cpuset.CPUSet { 186 var coreIDs []int 187 for _, id := range ids { 188 for _, info := range d { 189 if info.NUMANodeID == id { 190 coreIDs = append(coreIDs, info.CoreID) 191 } 192 } 193 } 194 return cpuset.New(coreIDs...) 195 } 196 197 // CoresInSockets returns all of the core IDs associated with the given socket 198 // IDs in this CPUDetails. 199 func (d CPUDetails) CoresInSockets(ids ...int) cpuset.CPUSet { 200 var coreIDs []int 201 for _, id := range ids { 202 for _, info := range d { 203 if info.SocketID == id { 204 coreIDs = append(coreIDs, info.CoreID) 205 } 206 } 207 } 208 return cpuset.New(coreIDs...) 209 } 210 211 // CPUs returns all of the logical CPU IDs in this CPUDetails. 212 func (d CPUDetails) CPUs() cpuset.CPUSet { 213 var cpuIDs []int 214 for cpuID := range d { 215 cpuIDs = append(cpuIDs, cpuID) 216 } 217 return cpuset.New(cpuIDs...) 218 } 219 220 // CPUsInNUMANodes returns all of the logical CPU IDs associated with the given 221 // NUMANode IDs in this CPUDetails. 222 func (d CPUDetails) CPUsInNUMANodes(ids ...int) cpuset.CPUSet { 223 var cpuIDs []int 224 for _, id := range ids { 225 for cpu, info := range d { 226 if info.NUMANodeID == id { 227 cpuIDs = append(cpuIDs, cpu) 228 } 229 } 230 } 231 return cpuset.New(cpuIDs...) 232 } 233 234 // CPUsInCores returns all of the logical CPU IDs associated with the given 235 // core IDs in this CPUDetails. 236 func (d CPUDetails) CPUsInCores(ids ...int) cpuset.CPUSet { 237 var cpuIDs []int 238 for _, id := range ids { 239 for cpu, info := range d { 240 if info.CoreID == id { 241 cpuIDs = append(cpuIDs, cpu) 242 } 243 } 244 } 245 return cpuset.New(cpuIDs...) 246 } 247 248 // Discover returns CPUTopology based on cadvisor node info 249 func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) { 250 if machineInfo.NumCores == 0 { 251 return nil, fmt.Errorf("could not detect number of cpus") 252 } 253 254 CPUDetails := CPUDetails{} 255 numPhysicalCores := 0 256 257 for _, node := range machineInfo.Topology { 258 numPhysicalCores += len(node.Cores) 259 for _, core := range node.Cores { 260 if coreID, err := getUniqueCoreID(core.Threads); err == nil { 261 for _, cpu := range core.Threads { 262 CPUDetails[cpu] = CPUInfo{ 263 CoreID: coreID, 264 SocketID: core.SocketID, 265 NUMANodeID: node.Id, 266 } 267 } 268 } else { 269 klog.ErrorS(nil, "Could not get unique coreID for socket", "socket", core.SocketID, "core", core.Id, "threads", core.Threads) 270 return nil, err 271 } 272 } 273 } 274 275 return &CPUTopology{ 276 NumCPUs: machineInfo.NumCores, 277 NumSockets: machineInfo.NumSockets, 278 NumCores: numPhysicalCores, 279 NumNUMANodes: CPUDetails.NUMANodes().Size(), 280 CPUDetails: CPUDetails, 281 }, nil 282 } 283 284 // getUniqueCoreID computes coreId as the lowest cpuID 285 // for a given Threads []int slice. This will assure that coreID's are 286 // platform unique (opposite to what cAdvisor reports) 287 func getUniqueCoreID(threads []int) (coreID int, err error) { 288 if len(threads) == 0 { 289 return 0, fmt.Errorf("no cpus provided") 290 } 291 292 if len(threads) != cpuset.New(threads...).Size() { 293 return 0, fmt.Errorf("cpus provided are not unique") 294 } 295 296 min := threads[0] 297 for _, thread := range threads[1:] { 298 if thread < min { 299 min = thread 300 } 301 } 302 303 return min, nil 304 }