Skip to content

Commit 63d749b

Browse files
committed
Add cgroupV2 CPUQuotaPeriodUSec support
Signed-off-by: Austin Vazquez <[email protected]>
1 parent 32dca23 commit 63d749b

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

cgroup2/cpu.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ type CPU struct {
3939
Mems string
4040
}
4141

42+
const (
43+
// Copied from https://github.com/opencontainers/cgroups/blob/9657f5a18b8d60a0f39fbb34d0cb7771e28e6278/systemd/common.go#L22-L25
44+
// Default kernel value for cpu quota period is 100000 us (100 ms), same for v1 and v2.
45+
// v1: https://www.kernel.org/doc/html/latest/scheduler/sched-bwc.html and
46+
// v2: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
47+
defCPUQuotaPeriod = uint64(100000)
48+
)
49+
4250
func (c CPUMax) extractQuotaAndPeriod() (int64, uint64) {
4351
var (
4452
quota int64
@@ -51,6 +59,10 @@ func (c CPUMax) extractQuotaAndPeriod() (int64, uint64) {
5159
quota, _ = strconv.ParseInt(values[0], 10, 64)
5260
}
5361
period, _ = strconv.ParseUint(values[1], 10, 64)
62+
if period == 0 {
63+
// assume the default
64+
period = defCPUQuotaPeriod
65+
}
5466
return quota, period
5567
}
5668

cgroup2/manager.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"path/filepath"
2828
"strconv"
2929
"strings"
30+
"sync"
3031
"time"
3132

3233
"github.com/containerd/cgroups/v3/cgroup2/stats"
@@ -47,7 +48,12 @@ const (
4748
defaultSlice = "system.slice"
4849
)
4950

50-
var canDelegate bool
51+
var (
52+
canDelegate bool
53+
54+
versionOnce sync.Once
55+
version int
56+
)
5157

5258
type Event struct {
5359
Low uint64
@@ -876,6 +882,16 @@ func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, e
876882

877883
if resources.CPU != nil && resources.CPU.Max != "" {
878884
quota, period := resources.CPU.Max.extractQuotaAndPeriod()
885+
if period != 0 {
886+
// systemd only supports CPUQuotaPeriodUSec since v242
887+
if sdVer := systemdVersion(conn); sdVer >= 242 {
888+
properties = append(properties, newSystemdProperty("CPUQuotaPeriodUSec", period))
889+
} else {
890+
log.G(context.TODO()).Debugf("systemd v%d is too old to support CPUQuotaPeriodSec "+
891+
" (setting will still be applied to cgroupfs)", sdVer)
892+
}
893+
}
894+
879895
// cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd.
880896
// corresponds to USEC_INFINITY in systemd
881897
// if USEC_INFINITY is provided, CPUQuota is left unbound by systemd
@@ -915,6 +931,41 @@ func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, e
915931
}, nil
916932
}
917933

934+
// Adapted from https://github.com/opencontainers/cgroups/blob/9657f5a18b8d60a0f39fbb34d0cb7771e28e6278/systemd/common.go#L245-L281
935+
func systemdVersion(conn *systemdDbus.Conn) int {
936+
versionOnce.Do(func() {
937+
version = -1
938+
verStr, err := conn.GetManagerProperty("Version")
939+
if err == nil {
940+
version, err = systemdVersionAtoi(verStr)
941+
}
942+
943+
if err != nil {
944+
log.G(context.TODO()).WithError(err).Error("Unable to get systemd version")
945+
}
946+
})
947+
948+
return version
949+
}
950+
951+
func systemdVersionAtoi(str string) (int, error) {
952+
// Unconditionally remove the leading prefix ("v).
953+
str = strings.TrimLeft(str, `"v`)
954+
// Match on the first integer we can grab.
955+
for i := range len(str) {
956+
if str[i] < '0' || str[i] > '9' {
957+
// First non-digit: cut the tail.
958+
str = str[:i]
959+
break
960+
}
961+
}
962+
ver, err := strconv.Atoi(str)
963+
if err != nil {
964+
return -1, fmt.Errorf("can't parse version: %w", err)
965+
}
966+
return ver, nil
967+
}
968+
918969
func startUnit(conn *systemdDbus.Conn, group string, properties []systemdDbus.Property, ignoreExists bool) error {
919970
ctx := context.TODO()
920971

0 commit comments

Comments
 (0)