cpu.go 1.3 KB

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