mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-04 05:34:20 +08:00
224 lines
6.0 KiB
Go
224 lines
6.0 KiB
Go
// Use and distribution licensed under the Apache license version 2.
|
|
//
|
|
// See the COPYING file in the root project directory for full text.
|
|
//
|
|
|
|
package cpu
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jaypipes/ghw/pkg/context"
|
|
"github.com/jaypipes/ghw/pkg/linuxpath"
|
|
"github.com/jaypipes/ghw/pkg/util"
|
|
)
|
|
|
|
var (
|
|
regexForCpulCore = regexp.MustCompile("^cpu([0-9]+)$")
|
|
)
|
|
|
|
func (i *Info) load() error {
|
|
i.Processors = processorsGet(i.ctx)
|
|
var totCores uint32
|
|
var totThreads uint32
|
|
for _, p := range i.Processors {
|
|
totCores += p.NumCores
|
|
totThreads += p.NumThreads
|
|
}
|
|
i.TotalCores = totCores
|
|
i.TotalThreads = totThreads
|
|
return nil
|
|
}
|
|
|
|
func ProcByID(procs []*Processor, id int) *Processor {
|
|
for pid := range procs {
|
|
if procs[pid].ID == id {
|
|
return procs[pid]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CoreByID(cores []*ProcessorCore, id int) *ProcessorCore {
|
|
for cid := range cores {
|
|
if cores[cid].Index == id {
|
|
return cores[cid]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func processorsGet(ctx *context.Context) []*Processor {
|
|
procs := make([]*Processor, 0)
|
|
paths := linuxpath.New(ctx)
|
|
|
|
r, err := os.Open(paths.ProcCpuinfo)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer util.SafeClose(r)
|
|
|
|
// An array of maps of attributes describing the logical processor
|
|
procAttrs := make([]map[string]string, 0)
|
|
curProcAttrs := make(map[string]string)
|
|
|
|
// Parse /proc/cpuinfo
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" {
|
|
// Output of /proc/cpuinfo has a blank newline to separate logical
|
|
// processors, so here we collect up all the attributes we've
|
|
// collected for this logical processor block
|
|
procAttrs = append(procAttrs, curProcAttrs)
|
|
// Reset the current set of processor attributes...
|
|
curProcAttrs = make(map[string]string)
|
|
continue
|
|
}
|
|
parts := strings.Split(line, ":")
|
|
key := strings.TrimSpace(parts[0])
|
|
value := strings.TrimSpace(parts[1])
|
|
curProcAttrs[key] = value
|
|
}
|
|
|
|
// Iterate on /sys/devices/system/cpu/cpuN, not on /proc/cpuinfo
|
|
Entries, err := ioutil.ReadDir(paths.SysDevicesSystemCPU)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
for _, lcore := range Entries {
|
|
matches := regexForCpulCore.FindStringSubmatch(lcore.Name())
|
|
if len(matches) < 2 {
|
|
continue
|
|
}
|
|
|
|
lcoreID, error := strconv.Atoi(matches[1])
|
|
if error != nil {
|
|
continue
|
|
}
|
|
|
|
// Fetch CPU ID
|
|
physIdPath := filepath.Join(paths.SysDevicesSystemCPU, fmt.Sprintf("cpu%d", lcoreID), "topology", "physical_package_id")
|
|
cpuID := util.SafeIntFromFile(ctx, physIdPath)
|
|
|
|
proc := ProcByID(procs, cpuID)
|
|
if proc == nil {
|
|
proc = &Processor{ID: cpuID}
|
|
// Assumes /proc/cpuinfo is in order of logical cpu id, then
|
|
// procAttrs[lcoreID] describes logical cpu `lcoreID`.
|
|
// Once got a more robust way of fetching the following info,
|
|
// can we drop /proc/cpuinfo.
|
|
if len(procAttrs[lcoreID]["flags"]) != 0 { // x86
|
|
proc.Capabilities = strings.Split(procAttrs[lcoreID]["flags"], " ")
|
|
} else if len(procAttrs[lcoreID]["Features"]) != 0 { // ARM64
|
|
proc.Capabilities = strings.Split(procAttrs[lcoreID]["Features"], " ")
|
|
}
|
|
if len(procAttrs[lcoreID]["model name"]) != 0 {
|
|
proc.Model = procAttrs[lcoreID]["model name"]
|
|
} else if len(procAttrs[lcoreID]["uarch"]) != 0 { // SiFive
|
|
proc.Model = procAttrs[lcoreID]["uarch"]
|
|
}
|
|
if len(procAttrs[lcoreID]["vendor_id"]) != 0 {
|
|
proc.Vendor = procAttrs[lcoreID]["vendor_id"]
|
|
} else if len(procAttrs[lcoreID]["isa"]) != 0 { // RISCV64
|
|
proc.Vendor = procAttrs[lcoreID]["isa"]
|
|
}
|
|
procs = append(procs, proc)
|
|
}
|
|
|
|
// Fetch Core ID
|
|
coreIdPath := filepath.Join(paths.SysDevicesSystemCPU, fmt.Sprintf("cpu%d", lcoreID), "topology", "core_id")
|
|
coreID := util.SafeIntFromFile(ctx, coreIdPath)
|
|
core := CoreByID(proc.Cores, coreID)
|
|
if core == nil {
|
|
core = &ProcessorCore{Index: coreID, NumThreads: 1}
|
|
proc.Cores = append(proc.Cores, core)
|
|
proc.NumCores += 1
|
|
} else {
|
|
core.NumThreads += 1
|
|
}
|
|
proc.NumThreads += 1
|
|
core.LogicalProcessors = append(core.LogicalProcessors, lcoreID)
|
|
}
|
|
return procs
|
|
}
|
|
|
|
func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) {
|
|
// The /sys/devices/system/node/nodeX directory contains a subdirectory
|
|
// called 'cpuX' for each logical processor assigned to the node. Each of
|
|
// those subdirectories contains a topology subdirectory which has a
|
|
// core_id file that indicates the 0-based identifier of the physical core
|
|
// the logical processor (hardware thread) is on.
|
|
paths := linuxpath.New(ctx)
|
|
path := filepath.Join(
|
|
paths.SysDevicesSystemNode,
|
|
fmt.Sprintf("node%d", nodeID),
|
|
)
|
|
cores := make([]*ProcessorCore, 0)
|
|
|
|
findCoreByID := func(coreID int) *ProcessorCore {
|
|
for _, c := range cores {
|
|
if c.ID == coreID {
|
|
return c
|
|
}
|
|
}
|
|
|
|
c := &ProcessorCore{
|
|
ID: coreID,
|
|
Index: len(cores),
|
|
LogicalProcessors: make([]int, 0),
|
|
}
|
|
cores = append(cores, c)
|
|
return c
|
|
}
|
|
|
|
files, err := ioutil.ReadDir(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, file := range files {
|
|
filename := file.Name()
|
|
if !strings.HasPrefix(filename, "cpu") {
|
|
continue
|
|
}
|
|
if filename == "cpumap" || filename == "cpulist" {
|
|
// There are two files in the node directory that start with 'cpu'
|
|
// but are not subdirectories ('cpulist' and 'cpumap'). Ignore
|
|
// these files.
|
|
continue
|
|
}
|
|
// Grab the logical processor ID by cutting the integer from the
|
|
// /sys/devices/system/node/nodeX/cpuX filename
|
|
cpuPath := filepath.Join(path, filename)
|
|
procID, err := strconv.Atoi(filename[3:])
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(
|
|
os.Stderr,
|
|
"failed to determine procID from %s. Expected integer after 3rd char.",
|
|
filename,
|
|
)
|
|
continue
|
|
}
|
|
coreIDPath := filepath.Join(cpuPath, "topology", "core_id")
|
|
coreID := util.SafeIntFromFile(ctx, coreIDPath)
|
|
core := findCoreByID(coreID)
|
|
core.LogicalProcessors = append(
|
|
core.LogicalProcessors,
|
|
procID,
|
|
)
|
|
}
|
|
|
|
for _, c := range cores {
|
|
c.NumThreads = uint32(len(c.LogicalProcessors))
|
|
}
|
|
|
|
return cores, nil
|
|
}
|