blob: 69b4cabd4e35f8cb3fdbc4ed1f1bc06a9a6b90ce [file] [log] [blame]
Filip Tehlar608d0062023-04-28 10:29:47 +02001package main
2
3import (
4 "bufio"
Matus Fabian18c9f142024-04-29 11:06:44 +02005 "errors"
Filip Tehlar608d0062023-04-28 10:29:47 +02006 "fmt"
Adrian Villin0df582e2024-05-22 09:26:47 -04007 . "github.com/onsi/ginkgo/v2"
Filip Tehlar608d0062023-04-28 10:29:47 +02008 "os"
Matus Fabian18c9f142024-04-29 11:06:44 +02009 "os/exec"
10 "strings"
Filip Tehlar608d0062023-04-28 10:29:47 +020011)
12
Matus Fabian18c9f142024-04-29 11:06:44 +020013var CgroupPath = "/sys/fs/cgroup/"
Filip Tehlar608d0062023-04-28 10:29:47 +020014
15type CpuContext struct {
16 cpuAllocator *CpuAllocatorT
17 cpus []int
18}
19
Filip Tehlar608d0062023-04-28 10:29:47 +020020type CpuAllocatorT struct {
21 cpus []int
22}
23
24var cpuAllocator *CpuAllocatorT = nil
25
Adrian Villin0df582e2024-05-22 09:26:47 -040026func (c *CpuAllocatorT) Allocate(vppContainerCount int, nCpus int) (*CpuContext, error) {
Filip Tehlar608d0062023-04-28 10:29:47 +020027 var cpuCtx CpuContext
Adrian Villin0df582e2024-05-22 09:26:47 -040028 maxCpu := GinkgoParallelProcess() * 2 * nCpus
29 minCpu := (GinkgoParallelProcess() - 1) * 2 * nCpus
30 if len(c.cpus) < maxCpu {
31 vppContainerCount += 1
32 err := fmt.Errorf("could not allocate %d CPUs; available: %d; attempted to allocate cores %d-%d",
33 nCpus*vppContainerCount, len(c.cpus), minCpu, minCpu+nCpus*vppContainerCount)
34 return nil, err
Filip Tehlar608d0062023-04-28 10:29:47 +020035 }
Adrian Villin0df582e2024-05-22 09:26:47 -040036 if vppContainerCount == 0 {
37 cpuCtx.cpus = c.cpus[minCpu : maxCpu-nCpus]
38 } else if vppContainerCount == 1 {
39 cpuCtx.cpus = c.cpus[minCpu+nCpus : maxCpu]
40 } else {
41 return nil, fmt.Errorf("too many VPP containers; CPU allocation for >2 VPP containers is not implemented yet")
42 }
43
Filip Tehlar608d0062023-04-28 10:29:47 +020044 cpuCtx.cpuAllocator = c
Filip Tehlar608d0062023-04-28 10:29:47 +020045 return &cpuCtx, nil
46}
47
Adrian Villincee15aa2024-03-14 11:42:55 -040048func (c *CpuAllocatorT) readCpus() error {
Filip Tehlar608d0062023-04-28 10:29:47 +020049 var first, last int
Matus Fabian18c9f142024-04-29 11:06:44 +020050
51 // Path depends on cgroup version. We need to check which version is in use.
52 // For that following command can be used: 'stat -fc %T /sys/fs/cgroup/'
53 // In case the output states 'cgroup2fs' then cgroups v2 is used, 'tmpfs' in case cgroups v1.
54 cmd := exec.Command("stat", "-fc", "%T", "/sys/fs/cgroup/")
55 byteOutput, err := cmd.CombinedOutput()
56 if err != nil {
57 return err
58 }
59 CpuPath := CgroupPath
60 if strings.Contains(string(byteOutput), "tmpfs") {
61 CpuPath += "cpuset/cpuset.effective_cpus"
62 } else if strings.Contains(string(byteOutput), "cgroup2fs") {
63 CpuPath += "cpuset.cpus.effective"
64 } else {
65 return errors.New("cgroup unknown fs: " + string(byteOutput))
66 }
67
68 file, err := os.Open(CpuPath)
Filip Tehlar608d0062023-04-28 10:29:47 +020069 if err != nil {
70 return err
71 }
72 defer file.Close()
73
74 sc := bufio.NewScanner(file)
75 sc.Scan()
76 line := sc.Text()
77 _, err = fmt.Sscanf(line, "%d-%d", &first, &last)
78 if err != nil {
79 return err
80 }
81 for i := first; i <= last; i++ {
82 c.cpus = append(c.cpus, i)
83 }
84 return nil
85}
86
87func CpuAllocator() (*CpuAllocatorT, error) {
88 if cpuAllocator == nil {
89 cpuAllocator = new(CpuAllocatorT)
Adrian Villincee15aa2024-03-14 11:42:55 -040090 err := cpuAllocator.readCpus()
Filip Tehlar608d0062023-04-28 10:29:47 +020091 if err != nil {
92 return nil, err
93 }
94 }
95 return cpuAllocator, nil
96}