cpu.go (1377B)
1 package internal 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "sync" 8 ) 9 10 var sysCPU struct { 11 once sync.Once 12 err error 13 num int 14 } 15 16 // PossibleCPUs returns the max number of CPUs a system may possibly have 17 // Logical CPU numbers must be of the form 0-n 18 func PossibleCPUs() (int, error) { 19 sysCPU.once.Do(func() { 20 sysCPU.num, sysCPU.err = parseCPUsFromFile("/sys/devices/system/cpu/possible") 21 }) 22 23 return sysCPU.num, sysCPU.err 24 } 25 26 func parseCPUsFromFile(path string) (int, error) { 27 spec, err := os.ReadFile(path) 28 if err != nil { 29 return 0, err 30 } 31 32 n, err := parseCPUs(string(spec)) 33 if err != nil { 34 return 0, fmt.Errorf("can't parse %s: %v", path, err) 35 } 36 37 return n, nil 38 } 39 40 // parseCPUs parses the number of cpus from a string produced 41 // by bitmap_list_string() in the Linux kernel. 42 // Multiple ranges are rejected, since they can't be unified 43 // into a single number. 44 // This is the format of /sys/devices/system/cpu/possible, it 45 // is not suitable for /sys/devices/system/cpu/online, etc. 46 func parseCPUs(spec string) (int, error) { 47 if strings.Trim(spec, "\n") == "0" { 48 return 1, nil 49 } 50 51 var low, high int 52 n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high) 53 if n != 2 || err != nil { 54 return 0, fmt.Errorf("invalid format: %s", spec) 55 } 56 if low != 0 { 57 return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec) 58 } 59 60 // cpus is 0 indexed 61 return high + 1, nil 62 }