diff --git a/utils.go b/utils.go index 98b6a07..95b3310 100644 --- a/utils.go +++ b/utils.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "os" "path/filepath" "strconv" @@ -413,16 +414,30 @@ func WriteCgroupProc(dir string, pid int) error { return err } -// Since the OCI spec is designed for cgroup v1, in some cases -// there is need to convert from the cgroup v1 configuration to cgroup v2 -// the formula for cpuShares is y = (1 + ((x - 2) * 9999) / 262142) -// convert from [2-262144] to [1-10000] -// 262144 comes from Linux kernel definition "#define MAX_SHARES (1UL << 18)" +// ConvertCPUSharesToCgroupV2Value converts CPU shares, used by cgroup v1, +// to CPU weight, used by cgroup v2. +// +// Cgroup v1 CPU shares has a range of [2^1...2^18], i.e. [2...262144], +// and the default value is 1024. +// +// Cgroup v2 CPU weight has a range of [10^0...10^4], i.e. [1...10000], +// and the default value is 100. func ConvertCPUSharesToCgroupV2Value(cpuShares uint64) uint64 { + // The value of 0 means "unset". if cpuShares == 0 { return 0 } - return (1 + ((cpuShares-2)*9999)/262142) + if cpuShares <= 2 { + return 1 + } + if cpuShares >= 262144 { + return 10000 + } + l := math.Log2(float64(cpuShares)) + // Quadratic function which fits min, max, and default. + exponent := (l*l+125*l)/612.0 - 7.0/34.0 + + return uint64(math.Ceil(math.Pow(10, exponent))) } // ConvertMemorySwapToCgroupV2Value converts MemorySwap value from OCI spec diff --git a/utils_test.go b/utils_test.go index 2d30373..79023b7 100644 --- a/utils_test.go +++ b/utils_test.go @@ -535,15 +535,30 @@ func TestGetHugePageSizeImpl(t *testing.T) { } func TestConvertCPUSharesToCgroupV2Value(t *testing.T) { + const ( + sharesMin = 2 + sharesMax = 262144 + sharesDef = 1024 + weightMin = 1 + weightMax = 10000 + weightDef = 100 + unset = 0 + ) cases := map[uint64]uint64{ - 0: 0, - 2: 1, - 262144: 10000, + unset: unset, + + sharesMin - 1: weightMin, // Below the minimum (out of range). + sharesMin: weightMin, // Minimum. + sharesMin + 1: weightMin + 1, // Just above the minimum. + sharesDef: weightDef, // Default. + sharesMax - 1: weightMax, // Just below the maximum. + sharesMax: weightMax, // Maximum. + sharesMax + 1: weightMax, // Above the maximum (out of range). } - for i, expected := range cases { - got := ConvertCPUSharesToCgroupV2Value(i) - if got != expected { - t.Errorf("expected ConvertCPUSharesToCgroupV2Value(%d) to be %d, got %d", i, expected, got) + for shares, want := range cases { + got := ConvertCPUSharesToCgroupV2Value(shares) + if got != want { + t.Errorf("ConvertCPUSharesToCgroupV2Value(%d): got %d, want %d", shares, got, want) } } }