From c6f5590c757b5cd2e9eb1c902b7f8e110a2fde0c Mon Sep 17 00:00:00 2001 From: David Ventura Date: Mon, 20 Dec 2021 11:45:11 +0100 Subject: [PATCH] Add isolated cpu parsing (#427) * Add isolated cpu parsing Signed-off-by: david * Address PR comments Signed-off-by: david * Fix start/end range message Signed-off-by: david * rename to parseIsolatedCPUs Signed-off-by: david * Do not wrap error Signed-off-by: david * also check error is nil Signed-off-by: david --- fixtures.ttar | 5 ++++ sysfs/system_cpu.go | 49 +++++++++++++++++++++++++++++++++++++ sysfs/system_cpu_test.go | 52 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/fixtures.ttar b/fixtures.ttar index e005ee94a..50294186f 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -6462,6 +6462,11 @@ Mode: 644 Directory: fixtures/sys/devices/system/cpu/cpufreq/policy1 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/system/cpu/isolated +Lines: 1 +1,2-7,9 +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/devices/system/node Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sysfs/system_cpu.go b/sysfs/system_cpu.go index 40fc94b43..7c46c2eaf 100644 --- a/sysfs/system_cpu.go +++ b/sysfs/system_cpu.go @@ -16,8 +16,11 @@ package sysfs import ( + "fmt" + "io/ioutil" "os" "path/filepath" + "strconv" "strings" "golang.org/x/sync/errgroup" @@ -237,3 +240,49 @@ func parseCpufreqCpuinfo(cpuPath string) (*SystemCPUCpufreqStats, error) { SetSpeed: stringOut[4], }, nil } + +func (fs FS) IsolatedCPUs() ([]uint16, error) { + isolcpus, err := ioutil.ReadFile(fs.sys.Path("devices/system/cpu/isolated")) + if err != nil { + return nil, err + } + + return parseIsolatedCPUs(isolcpus) +} + +func parseIsolatedCPUs(data []byte) ([]uint16, error) { + + var isolcpusInt = []uint16{} + + for _, cpu := range strings.Split(strings.TrimRight(string(data), "\n"), ",") { + if cpu == "" { + continue + } + if strings.Contains(cpu, "-") { + ranges := strings.Split(cpu, "-") + if len(ranges) != 2 { + return nil, fmt.Errorf("invalid cpu range: %s", cpu) + } + startRange, err := strconv.Atoi(ranges[0]) + if err != nil { + return nil, fmt.Errorf("invalid cpu start range: %w", err) + } + endRange, err := strconv.Atoi(ranges[1]) + if err != nil { + return nil, fmt.Errorf("invalid cpu end range: %w", err) + } + + for i := startRange; i <= endRange; i++ { + isolcpusInt = append(isolcpusInt, uint16(i)) + } + continue + } + + cpuN, err := strconv.Atoi(cpu) + if err != nil { + return nil, err + } + isolcpusInt = append(isolcpusInt, uint16(cpuN)) + } + return isolcpusInt, nil +} diff --git a/sysfs/system_cpu_test.go b/sysfs/system_cpu_test.go index 522b58b56..0ae9f0fd9 100644 --- a/sysfs/system_cpu_test.go +++ b/sysfs/system_cpu_test.go @@ -16,6 +16,7 @@ package sysfs import ( + "errors" "reflect" "testing" ) @@ -140,3 +141,54 @@ func TestSystemCpufreq(t *testing.T) { t.Errorf("Result not correct: want %v, have %v", systemCpufreq, c) } } + +func TestIsolatedParsingCPU(t *testing.T) { + var testParams = []struct { + in []byte + res []uint16 + err error + }{ + {[]byte(""), []uint16{}, nil}, + {[]byte("1\n"), []uint16{1}, nil}, + {[]byte("1"), []uint16{1}, nil}, + {[]byte("1,2"), []uint16{1, 2}, nil}, + {[]byte("1-2"), []uint16{1, 2}, nil}, + {[]byte("1-3"), []uint16{1, 2, 3}, nil}, + {[]byte("1,2-4"), []uint16{1, 2, 3, 4}, nil}, + {[]byte("1,3-4"), []uint16{1, 3, 4}, nil}, + {[]byte("1,3-4,7,20-21"), []uint16{1, 3, 4, 7, 20, 21}, nil}, + + {[]byte("1,"), []uint16{1}, nil}, + {[]byte("1,2-"), nil, errors.New(`invalid cpu end range: strconv.Atoi: parsing "": invalid syntax`)}, + {[]byte("1,-3"), nil, errors.New(`invalid cpu start range: strconv.Atoi: parsing "": invalid syntax`)}, + } + for _, params := range testParams { + t.Run("blabla", func(t *testing.T) { + res, err := parseIsolatedCPUs(params.in) + if !reflect.DeepEqual(res, params.res) { + t.Fatalf("should have %v result: got %v", params.res, res) + } + if err != nil && params.err != nil && err.Error() != params.err.Error() { + t.Fatalf("should have '%v' error: got '%v'", params.err, err) + } + if (err == nil || params.err == nil) && err != params.err { + t.Fatalf("should have %v error: got %v", params.err, err) + } + + }) + } +} +func TestIsolatedCPUs(t *testing.T) { + fs, err := NewFS(sysTestFixtures) + if err != nil { + t.Fatal(err) + } + isolated, err := fs.IsolatedCPUs() + expected := []uint16{1, 2, 3, 4, 5, 6, 7, 9} + if !reflect.DeepEqual(isolated, expected) { + t.Errorf("Result not correct: want %v, have %v", expected, isolated) + } + if err != nil { + t.Errorf("Error not correct: want %v, have %v", nil, err) + } +}