Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 20 additions & 29 deletions validation/linux_cgroups_hugetlb/linux_cgroups_hugetlb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"os"
"runtime"

"github.com/mndrix/tap-go"
Expand All @@ -10,16 +11,7 @@ import (
"github.com/opencontainers/runtime-tools/validation/util"
)

func testHugetlbCgroups() error {
t := tap.New()
t.Header(0)
defer t.AutoPlan()

pageSizes, err := cgroups.GetHugePageSize()
if err != nil {
t.Fail(fmt.Sprintf("error when getting hugepage sizes: %+v", err))
}

func testHugetlbCgroups(t *tap.T, pageSizes []string) {
// When setting the limit just for checking if writing works, the amount of memory
// requested does not matter, as all insigned integers will be accepted.
// Use 2GiB as an example
Expand All @@ -28,7 +20,7 @@ func testHugetlbCgroups() error {
for _, pageSize := range pageSizes {
g, err := util.GetDefaultGenerator()
if err != nil {
return err
util.Fatal(err)
}
g.SetLinuxCgroupsPath(cgroups.AbsCgroupPath)
g.AddLinuxResourcesHugepageLimit(pageSize, limit)
Expand All @@ -51,14 +43,12 @@ func testHugetlbCgroups() error {
return nil
})
if err != nil {
return err
t.Fail(err.Error())
}
}

return nil
}

func testWrongHugetlb() error {
func testWrongHugetlb(t *tap.T) {
// We deliberately set the page size to a wrong value, "3MB", to see
// if the container really returns an error. Page sizes will always be a
// on the format 2^(integer)
Expand All @@ -67,36 +57,37 @@ func testWrongHugetlb() error {

g, err := util.GetDefaultGenerator()
if err != nil {
return err
util.Fatal(err)
}

t := tap.New()
t.Header(0)
defer t.AutoPlan()

g.SetLinuxCgroupsPath(cgroups.AbsCgroupPath)
g.AddLinuxResourcesHugepageLimit(page, limit)

err = util.RuntimeOutsideValidate(g, t, func(config *rspec.Spec, t *tap.T, state *rspec.State) error {
return nil
})
err = util.RuntimeOutsideValidate(g, t, nil)
t.Ok(err != nil, "hugepage invalid pagesize results in an errror")
if err == nil {
t.Diagnosticf("expect: err != nil, actual: err == nil")
}
return err
}

func main() {
if runtime.GOOS != "linux" {
util.Fatal(fmt.Errorf("linux-specific cgroup test"))
}

if err := testHugetlbCgroups(); err != nil {
util.Fatal(err)
pageSizes, err := util.GetHugetlbPageSizes()
if err != nil {
util.Fatal(fmt.Errorf("error when getting hugepage sizes: %+v", err))
}

if err := testWrongHugetlb(); err == nil {
util.Fatal(err)
if len(pageSizes) == 0 {
util.Skip("no huge pages or hugetlb cgroup controller on the host", nil)
os.Exit(0)
}

t := tap.New()
t.Header(0)
defer t.AutoPlan()

testHugetlbCgroups(t, pageSizes)
testWrongHugetlb(t)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"os"

"github.com/mndrix/tap-go"
rspec "github.com/opencontainers/runtime-spec/specs-go"
Expand All @@ -10,14 +11,19 @@ import (
)

func main() {
pageSizes, err := util.GetHugetlbPageSizes()
if err != nil {
util.Fatal(fmt.Errorf("error when getting hugepage sizes: %+v", err))
}
if len(pageSizes) == 0 {
util.Skip("no huge pages or hugetlb cgroup controller on the host", nil)
os.Exit(0)
}

t := tap.New()
t.Header(0)
defer t.AutoPlan()

pageSizes, err := cgroups.GetHugePageSize()
if err != nil {
t.Fail(fmt.Sprintf("error when getting hugepage sizes: %+v", err))
}
// When setting the limit just for checking if writing works, the amount of memory
// requested does not matter, as all insigned integers will be accepted.
// Use 2GiB as an example
Expand Down
29 changes: 22 additions & 7 deletions validation/process_rlimits/process_rlimits.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,30 @@ func main() {
util.Fatal(err)
}

large, err := util.LargeRlimitsSupported()
if err != nil {
util.Fatal(err)
}

var gigaBytes uint64 = 1024 * 1024 * 1024
g.AddProcessRlimits("RLIMIT_AS", 2*gigaBytes, 1*gigaBytes)
g.AddProcessRlimits("RLIMIT_CORE", 4*gigaBytes, 3*gigaBytes)
g.AddProcessRlimits("RLIMIT_DATA", 6*gigaBytes, 5*gigaBytes)
g.AddProcessRlimits("RLIMIT_FSIZE", 8*gigaBytes, 7*gigaBytes)
g.AddProcessRlimits("RLIMIT_STACK", 10*gigaBytes, 9*gigaBytes)
for _, r := range []struct {
rlimit string
hard, soft uint64
}{
{"RLIMIT_AS", 2 * gigaBytes, 1 * gigaBytes},
{"RLIMIT_CORE", 4 * gigaBytes, 3 * gigaBytes},
{"RLIMIT_DATA", 6 * gigaBytes, 5 * gigaBytes},
{"RLIMIT_FSIZE", 8 * gigaBytes, 7 * gigaBytes},
{"RLIMIT_STACK", 10 * gigaBytes, 9 * gigaBytes},
{"RLIMIT_CPU", 120, 60}, // seconds
{"RLIMIT_NOFILE", 4000, 3000}, // number of files
} {
if !large && r.hard >= 4*gigaBytes {
continue
}
g.AddProcessRlimits(r.rlimit, r.hard, r.soft)
}

g.AddProcessRlimits("RLIMIT_CPU", 120, 60) // seconds
g.AddProcessRlimits("RLIMIT_NOFILE", 4000, 3000) // number of files
err = util.RuntimeInsideValidate(g, nil, nil)
if err != nil {
util.Fatal(err)
Expand Down
50 changes: 50 additions & 0 deletions validation/util/linux_resources_hugetlb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package util

import (
"bufio"
"errors"
"io"
"os"
"strings"

"github.com/opencontainers/runtime-tools/cgroups"
)

// GetHugetlbPageSizes returns the huge page sizes of the host. The list is
// empty when the kernel offers no huge pages or no hugetlb cgroup
// controller, in which case no runtime can apply hugepage limits.
func GetHugetlbPageSizes() ([]string, error) {
enabled, err := cgroupControllerEnabled("hugetlb")
if err != nil || !enabled {
return nil, err
}

pageSizes, err := cgroups.GetHugePageSize()
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
return pageSizes, err
}

// cgroupControllerEnabled reports whether the kernel provides the named
// cgroup controller, as listed in /proc/cgroups for both cgroup versions.
func cgroupControllerEnabled(name string) (bool, error) {
f, err := os.Open("/proc/cgroups")
if err != nil {
return false, err
}
defer f.Close()

return controllerEnabled(f, name)
}

func controllerEnabled(r io.Reader, name string) (bool, error) {
s := bufio.NewScanner(r)
for s.Scan() {
fields := strings.Fields(s.Text())
if len(fields) == 4 && fields[0] == name {
return fields[3] == "1", nil
}
}
return false, s.Err()
}
33 changes: 33 additions & 0 deletions validation/util/linux_resources_hugetlb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package util

import (
"strings"
"testing"
)

const procCgroups = `#subsys_name hierarchy num_cgroups enabled
cpuset 0 12 1
cpu 0 12 1
memory 0 12 0
pids 0 12 1
`

func TestControllerEnabled(t *testing.T) {
for _, tc := range []struct {
name string
enabled bool
}{
{"cpu", true},
{"pids", true},
{"memory", false},
{"hugetlb", false},
} {
enabled, err := controllerEnabled(strings.NewReader(procCgroups), tc.name)
if err != nil {
t.Fatalf("%s: %v", tc.name, err)
}
if enabled != tc.enabled {
t.Errorf("%s: enabled %v, want %v", tc.name, enabled, tc.enabled)
}
}
}
34 changes: 34 additions & 0 deletions validation/util/rlimit_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package util

import (
"errors"

"golang.org/x/sys/unix"
)

// LargeRlimitsSupported reports whether resource limits of 4 GiB and more
// can be set and read back on this host. A 32-bit kernel stores them in an
// unsigned long and turns any larger value into RLIM_INFINITY, and without
// CAP_SYS_RESOURCE a hard limit below 4 GiB cannot be raised to them.
func LargeRlimitsSupported() (bool, error) {
const probe = 1 << 32
var old, cur unix.Rlimit

if err := unix.Prlimit(0, unix.RLIMIT_FSIZE, nil, &old); err != nil {
return false, err
}

cur = unix.Rlimit{Cur: probe, Max: max(old.Max, probe)}
if err := unix.Prlimit(0, unix.RLIMIT_FSIZE, &cur, nil); err != nil {
if errors.Is(err, unix.EPERM) {
return false, nil
}
return false, err
}
defer unix.Prlimit(0, unix.RLIMIT_FSIZE, &old, nil)

if err := unix.Prlimit(0, unix.RLIMIT_FSIZE, nil, &cur); err != nil {
return false, err
}
return cur.Cur == probe, nil
}
24 changes: 24 additions & 0 deletions validation/util/rlimit_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package util

import (
"testing"

"golang.org/x/sys/unix"
)

func TestLargeRlimitsSupportedRestores(t *testing.T) {
var before, after unix.Rlimit

if err := unix.Prlimit(0, unix.RLIMIT_FSIZE, nil, &before); err != nil {
t.Fatal(err)
}
if _, err := LargeRlimitsSupported(); err != nil {
t.Fatal(err)
}
if err := unix.Prlimit(0, unix.RLIMIT_FSIZE, nil, &after); err != nil {
t.Fatal(err)
}
if before != after {
t.Errorf("RLIMIT_FSIZE changed from %+v to %+v", before, after)
}
}
9 changes: 9 additions & 0 deletions validation/util/rlimit_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !linux

package util

// LargeRlimitsSupported reports whether the host kernel holds resource
// limits of 4 GiB and more.
func LargeRlimitsSupported() (bool, error) {
return true, nil
}