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