123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package topology
- import (
- "fmt"
- "io/ioutil"
- "strings"
- cadvisorapi "github.com/google/cadvisor/info/v1"
- "k8s.io/klog"
- "k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
- )
- // NUMANodeInfo is a map from NUMANode ID to a list of CPU IDs associated with
- // that NUMANode.
- type NUMANodeInfo map[int]cpuset.CPUSet
- // CPUDetails is a map from CPU ID to Core ID, Socket ID, and NUMA ID.
- type CPUDetails map[int]CPUInfo
- // CPUTopology contains details of node cpu, where :
- // CPU - logical CPU, cadvisor - thread
- // Core - physical CPU, cadvisor - Core
- // Socket - socket, cadvisor - Node
- type CPUTopology struct {
- NumCPUs int
- NumCores int
- NumSockets int
- CPUDetails CPUDetails
- }
- // CPUsPerCore returns the number of logical CPUs are associated with
- // each core.
- func (topo *CPUTopology) CPUsPerCore() int {
- if topo.NumCores == 0 {
- return 0
- }
- return topo.NumCPUs / topo.NumCores
- }
- // CPUsPerSocket returns the number of logical CPUs are associated with
- // each socket.
- func (topo *CPUTopology) CPUsPerSocket() int {
- if topo.NumSockets == 0 {
- return 0
- }
- return topo.NumCPUs / topo.NumSockets
- }
- // CPUInfo contains the NUMA, socket, and core IDs associated with a CPU.
- type CPUInfo struct {
- NUMANodeID int
- SocketID int
- CoreID int
- }
- // KeepOnly returns a new CPUDetails object with only the supplied cpus.
- func (d CPUDetails) KeepOnly(cpus cpuset.CPUSet) CPUDetails {
- result := CPUDetails{}
- for cpu, info := range d {
- if cpus.Contains(cpu) {
- result[cpu] = info
- }
- }
- return result
- }
- // NUMANodes returns all of the NUMANode IDs associated with the CPUs in this
- // CPUDetails.
- func (d CPUDetails) NUMANodes() cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, info := range d {
- b.Add(info.NUMANodeID)
- }
- return b.Result()
- }
- // NUMANodesInSockets returns all of the logical NUMANode IDs associated with
- // the given socket IDs in this CPUDetails.
- func (d CPUDetails) NUMANodesInSockets(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for _, info := range d {
- if info.SocketID == id {
- b.Add(info.NUMANodeID)
- }
- }
- }
- return b.Result()
- }
- // Sockets returns all of the socket IDs associated with the CPUs in this
- // CPUDetails.
- func (d CPUDetails) Sockets() cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, info := range d {
- b.Add(info.SocketID)
- }
- return b.Result()
- }
- // CPUsInSockets returns all of the logical CPU IDs associated with the given
- // socket IDs in this CPUDetails.
- func (d CPUDetails) CPUsInSockets(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for cpu, info := range d {
- if info.SocketID == id {
- b.Add(cpu)
- }
- }
- }
- return b.Result()
- }
- // SocketsInNUMANodes returns all of the logical Socket IDs associated with the
- // given NUMANode IDs in this CPUDetails.
- func (d CPUDetails) SocketsInNUMANodes(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for _, info := range d {
- if info.NUMANodeID == id {
- b.Add(info.SocketID)
- }
- }
- }
- return b.Result()
- }
- // Cores returns all of the core IDs associated with the CPUs in this
- // CPUDetails.
- func (d CPUDetails) Cores() cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, info := range d {
- b.Add(info.CoreID)
- }
- return b.Result()
- }
- // CoresInNUMANodes returns all of the core IDs associated with the given
- // NUMANode IDs in this CPUDetails.
- func (d CPUDetails) CoresInNUMANodes(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for _, info := range d {
- if info.NUMANodeID == id {
- b.Add(info.CoreID)
- }
- }
- }
- return b.Result()
- }
- // CoresInSockets returns all of the core IDs associated with the given socket
- // IDs in this CPUDetails.
- func (d CPUDetails) CoresInSockets(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for _, info := range d {
- if info.SocketID == id {
- b.Add(info.CoreID)
- }
- }
- }
- return b.Result()
- }
- // CPUs returns all of the logical CPU IDs in this CPUDetails.
- func (d CPUDetails) CPUs() cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for cpuID := range d {
- b.Add(cpuID)
- }
- return b.Result()
- }
- // CPUsInNUMANodes returns all of the logical CPU IDs associated with the given
- // NUMANode IDs in this CPUDetails.
- func (d CPUDetails) CPUsInNUMANodes(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for cpu, info := range d {
- if info.NUMANodeID == id {
- b.Add(cpu)
- }
- }
- }
- return b.Result()
- }
- // CPUsInCores returns all of the logical CPU IDs associated with the given
- // core IDs in this CPUDetails.
- func (d CPUDetails) CPUsInCores(ids ...int) cpuset.CPUSet {
- b := cpuset.NewBuilder()
- for _, id := range ids {
- for cpu, info := range d {
- if info.CoreID == id {
- b.Add(cpu)
- }
- }
- }
- return b.Result()
- }
- // Discover returns CPUTopology based on cadvisor node info
- func Discover(machineInfo *cadvisorapi.MachineInfo, numaNodeInfo NUMANodeInfo) (*CPUTopology, error) {
- if machineInfo.NumCores == 0 {
- return nil, fmt.Errorf("could not detect number of cpus")
- }
- CPUDetails := CPUDetails{}
- numPhysicalCores := 0
- for _, socket := range machineInfo.Topology {
- numPhysicalCores += len(socket.Cores)
- for _, core := range socket.Cores {
- if coreID, err := getUniqueCoreID(core.Threads); err == nil {
- for _, cpu := range core.Threads {
- numaNodeID := 0
- for id, cset := range numaNodeInfo {
- if cset.Contains(cpu) {
- numaNodeID = id
- }
- }
- CPUDetails[cpu] = CPUInfo{
- CoreID: coreID,
- SocketID: socket.Id,
- NUMANodeID: numaNodeID,
- }
- }
- } else {
- klog.Errorf("could not get unique coreID for socket: %d core %d threads: %v",
- socket.Id, core.Id, core.Threads)
- return nil, err
- }
- }
- }
- return &CPUTopology{
- NumCPUs: machineInfo.NumCores,
- NumSockets: len(machineInfo.Topology),
- NumCores: numPhysicalCores,
- CPUDetails: CPUDetails,
- }, nil
- }
- // getUniqueCoreID computes coreId as the lowest cpuID
- // for a given Threads []int slice. This will assure that coreID's are
- // platform unique (opposite to what cAdvisor reports - socket unique)
- func getUniqueCoreID(threads []int) (coreID int, err error) {
- if len(threads) == 0 {
- return 0, fmt.Errorf("no cpus provided")
- }
- if len(threads) != cpuset.NewCPUSet(threads...).Size() {
- return 0, fmt.Errorf("cpus provided are not unique")
- }
- min := threads[0]
- for _, thread := range threads[1:] {
- if thread < min {
- min = thread
- }
- }
- return min, nil
- }
- // GetNUMANodeInfo uses sysfs to return a map of NUMANode id to the list of
- // CPUs associated with that NUMANode.
- //
- // TODO: This is a temporary workaround until cadvisor provides this
- // information directly in machineInfo. We should remove this once this
- // information is available from cadvisor.
- func GetNUMANodeInfo() (NUMANodeInfo, error) {
- // Get the possible NUMA nodes on this machine. If reading this file
- // is not possible, this is not an error. Instead, we just return a
- // nil NUMANodeInfo, indicating that no NUMA information is available
- // on this machine. This should implicitly be interpreted as having a
- // single NUMA node with id 0 for all CPUs.
- nodelist, err := ioutil.ReadFile("/sys/devices/system/node/online")
- if err != nil {
- return nil, nil
- }
- // Parse the nodelist into a set of Node IDs
- nodes, err := cpuset.Parse(strings.TrimSpace(string(nodelist)))
- if err != nil {
- return nil, err
- }
- info := make(NUMANodeInfo)
- // For each node...
- for _, node := range nodes.ToSlice() {
- // Read the 'cpulist' of the NUMA node from sysfs.
- path := fmt.Sprintf("/sys/devices/system/node/node%d/cpulist", node)
- cpulist, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, err
- }
- // Convert the 'cpulist' into a set of CPUs.
- cpus, err := cpuset.Parse(strings.TrimSpace(string(cpulist)))
- if err != nil {
- return nil, err
- }
- info[node] = cpus
- }
- return info, nil
- }
|