diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 037a79bb..6fccd0aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,7 @@ jobs: uses: docker/setup-buildx-action@v1 with: config: .github/buildkit.toml + buildkitd-flags: --allow-insecure-entitlement=security.insecure - name: Test buildkit if: matrix.target == 'buildkit' @@ -110,6 +111,11 @@ jobs: docker run --rm --platform=linux/s390x s390x/ubuntu apt update docker run --rm --platform=linux/ppc64le ppc64le/ubuntu apt update docker run --rm --platform=linux/arm64 arm64v8/ubuntu apt update + - name: Test Syscalls + if: matrix.target == 'mainline' + run: | + set -x + docker buildx build --platform=linux/amd64,linux/arm64,linux/386,linux/arm,linux/ppc64le,linux/s390x --target=run --allow security.insecure --build-arg CONFIG_RT_GROUP_SCHED=false ./test - name: Login to DockerHub if: startsWith(github.ref, 'refs/heads/') diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 00000000..9887f312 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,30 @@ +#syntax=docker/dockerfile:1.3-labs + +FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx + +FROM scratch AS src +COPY *.go go.* / + +FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS build +COPY --from=xx / / +RUN apk add clang lld file +ARG TARGETPLATFORM +RUN xx-apk add musl-dev linux-headers gcc +WORKDIR /src +RUN XX_CC_PREFER_STATIC_LINKER=1 xx-clang --setup-target-triple +RUN --mount=from=src \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + CGO_ENABLED=1 xx-go test -c -o /out/test -ldflags "-linkmode external -extldflags -static" . && \ + xx-verify --static /out/test + +FROM scratch AS binary +COPY --from=build /out/test . + +FROM alpine AS run +RUN apk add libcap +COPY --from=binary / /usr/bin +ARG CONFIG_RT_GROUP_SCHED +RUN --security=insecure /usr/bin/test -test.v + +FROM binary \ No newline at end of file diff --git a/test/go.mod b/test/go.mod new file mode 100644 index 00000000..f1eee0a9 --- /dev/null +++ b/test/go.mod @@ -0,0 +1,14 @@ +module github.com/tonistiigi/binfmt/test + +go 1.17 + +require ( + github.com/stretchr/testify v1.7.0 + golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 +) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/test/go.sum b/test/go.sum new file mode 100644 index 00000000..4c07c56f --- /dev/null +++ b/test/go.sum @@ -0,0 +1,13 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/sched.go b/test/sched.go new file mode 100644 index 00000000..3f383ddf --- /dev/null +++ b/test/sched.go @@ -0,0 +1,122 @@ +package tests + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// #include +// #include +// typedef struct sched_param sched_param; +import "C" + +type Policy uint32 + +const ( + SCHED_NORMAL Policy = C.SCHED_NORMAL + SCHED_FIFO Policy = C.SCHED_FIFO + SCHED_RR Policy = C.SCHED_RR + SCHED_BATCH Policy = C.SCHED_BATCH + SCHED_IDLE Policy = C.SCHED_IDLE + SCHED_DEADLINE Policy = C.SCHED_DEADLINE +) + +type SchedFlag int + +const ( + SCHED_FLAG_RESET_ON_FORK SchedFlag = C.SCHED_FLAG_RESET_ON_FORK + SCHED_FLAG_RECLAIM SchedFlag = C.SCHED_FLAG_RECLAIM + SCHED_FLAG_DL_OVERRUN SchedFlag = C.SCHED_FLAG_DL_OVERRUN +) + +type SchedParam C.sched_param + +func SchedGetScheduler(pid int) (Policy, error) { + r0, _, e1 := unix.Syscall(unix.SYS_SCHED_GETSCHEDULER, uintptr(pid), 0, 0) + if e1 != 0 { + return 0, syscall.Errno(e1) + } + return Policy(r0), nil +} + +func SchedSetScheduler(pid int, p Policy, param SchedParam) error { + _, _, e1 := unix.Syscall(unix.SYS_SCHED_SETSCHEDULER, uintptr(pid), uintptr(p), uintptr(unsafe.Pointer(¶m))) + if e1 != 0 { + return syscall.Errno(e1) + } + return nil +} + +func SchedGetPriorityMin(p Policy) (int, error) { + r0, _, e1 := unix.Syscall(unix.SYS_SCHED_GET_PRIORITY_MIN, uintptr(p), 0, 0) + if e1 != 0 { + return 0, syscall.Errno(e1) + } + return int(r0), nil +} + +func SchedGetPriorityMax(p Policy) (int, error) { + r0, _, e1 := unix.Syscall(unix.SYS_SCHED_GET_PRIORITY_MAX, uintptr(p), 0, 0) + if e1 != 0 { + return 0, syscall.Errno(e1) + } + return int(r0), nil +} + +func SchedYield() error { + _, _, e1 := unix.Syscall(unix.SYS_SCHED_YIELD, 0, 0, 0) + if e1 != 0 { + return syscall.Errno(e1) + } + return nil +} + +func SchedGetParam(pid int) (SchedParam, error) { + var param SchedParam + _, _, e1 := unix.Syscall(unix.SYS_SCHED_GETPARAM, uintptr(pid), uintptr(unsafe.Pointer(¶m)), 0) + if e1 != 0 { + return param, syscall.Errno(e1) + } + return param, nil +} + +func SchedSetParam(pid int, param SchedParam) error { + _, _, e1 := unix.Syscall(unix.SYS_SCHED_SETPARAM, uintptr(pid), uintptr(unsafe.Pointer(¶m)), 0) + if e1 != 0 { + return syscall.Errno(e1) + } + return nil +} + +type SchedAttr struct { + Size uint32 + SchedPolicy Policy + SchedFlags uint64 + SchedNice uint32 + SchedPriority uint32 + SchedRuntime uint64 + SchedDeadline uint64 + SchedPeriod uint64 + SchedUtilMin uint32 + SchedUtilMax uint32 +} + +func SchedGetAttr(pid int) (SchedAttr, error) { + var attr SchedAttr + _, _, e1 := unix.Syscall6(unix.SYS_SCHED_GETATTR, uintptr(pid), uintptr(unsafe.Pointer(&attr)), unsafe.Sizeof(SchedAttr{}), 0, 0, 0) + if e1 != 0 { + return attr, syscall.Errno(e1) + } + return attr, nil +} + +func SchedSetAttr(pid int, attr SchedAttr, flags SchedFlag) error { + attr.Size = uint32(unsafe.Sizeof(attr)) + _, _, e1 := unix.Syscall(unix.SYS_SCHED_SETATTR, uintptr(pid), uintptr(unsafe.Pointer(&attr)), uintptr(flags)) + if e1 != 0 { + return syscall.Errno(e1) + } + return nil +} diff --git a/test/sched_test.go b/test/sched_test.go new file mode 100644 index 00000000..35e87071 --- /dev/null +++ b/test/sched_test.go @@ -0,0 +1,152 @@ +package tests + +import ( + "errors" + "os" + "runtime" + "strconv" + "syscall" + "testing" + + "github.com/stretchr/testify/require" +) + +var CONFIG_RT_GROUP_SCHED bool = true + +func init() { + runtime.LockOSThread() + + if v := os.Getenv("CONFIG_RT_GROUP_SCHED"); v != "" { + if vv, err := strconv.ParseBool(v); err == nil { + CONFIG_RT_GROUP_SCHED = vv + } + } +} + +func TestGetSetScheduler(t *testing.T) { + _, err := SchedGetScheduler(1 << 30) + require.Error(t, err) + + pid := os.Getpid() + s, err := SchedGetScheduler(pid) + require.NoError(t, err) + require.Equal(t, SCHED_NORMAL, s) + + // priority + err = SchedSetScheduler(pid, SCHED_RR, SchedParam{ + sched_priority: 100, + }) + require.True(t, errors.Is(err, syscall.EINVAL)) + + err = SchedSetScheduler(pid, SCHED_RR, SchedParam{ + sched_priority: 90, + }) + require.True(t, !errors.Is(err, syscall.EINVAL)) + + if CONFIG_RT_GROUP_SCHED { + t.Logf("skipping scheduler set checks") + } else { + require.NoError(t, err) + + s, err := SchedGetScheduler(pid) + require.NoError(t, err) + require.Equal(t, SCHED_RR, s) + + p, err := SchedGetParam(pid) + require.NoError(t, err) + require.Equal(t, 90, int(p.sched_priority)) + } + + err = SchedSetScheduler(pid, SCHED_IDLE, SchedParam{}) + require.NoError(t, err) + + s, err = SchedGetScheduler(pid) + require.NoError(t, err) + require.Equal(t, SCHED_IDLE, s) + + err = SchedSetScheduler(pid, SCHED_NORMAL, SchedParam{}) + require.NoError(t, err) +} + +func TestSchedMinMax(t *testing.T) { + p, err := SchedGetPriorityMin(SCHED_RR) + require.NoError(t, err) + require.Equal(t, 1, p) + + p, err = SchedGetPriorityMax(SCHED_RR) + require.NoError(t, err) + require.Equal(t, 99, p) + + p, err = SchedGetPriorityMax(SCHED_BATCH) + require.NoError(t, err) + require.Equal(t, 0, p) +} + +func TestSchedYield(t *testing.T) { + err := SchedYield() + require.NoError(t, err) +} + +func TestSchedGetSetParam(t *testing.T) { + pid := os.Getpid() + + p, err := SchedGetParam(pid) + require.NoError(t, err) + + require.Equal(t, 0, int(p.sched_priority)) + + err = SchedSetParam(pid, SchedParam{}) + require.NoError(t, err) + + if CONFIG_RT_GROUP_SCHED { + t.Logf("skipping setting sched_priority") + } else { + err = SchedSetScheduler(pid, SCHED_RR, SchedParam{sched_priority: 50}) + require.NoError(t, err) + + p, err := SchedGetParam(pid) + require.NoError(t, err) + + require.Equal(t, 50, int(p.sched_priority)) + } +} + +func TestSchedAttr(t *testing.T) { + pid := os.Getpid() + + if !CONFIG_RT_GROUP_SCHED { + err := SchedSetScheduler(pid, SCHED_RR, SchedParam{sched_priority: 50}) + require.NoError(t, err) + } + + attr, err := SchedGetAttr(pid) + require.NoError(t, err) + + require.True(t, attr.Size >= 0x30) + + if CONFIG_RT_GROUP_SCHED { + require.Equal(t, SCHED_NORMAL, attr.SchedPolicy) + + attr := SchedAttr{SchedPolicy: SCHED_IDLE} + err := SchedSetAttr(pid, attr, 0) + require.NoError(t, err) + + attr, err = SchedGetAttr(pid) + require.NoError(t, err) + require.Equal(t, SCHED_IDLE, attr.SchedPolicy) + + t.Logf("skipping setting attr priority") + } else { + require.Equal(t, SCHED_RR, attr.SchedPolicy) + require.Equal(t, 50, int(attr.SchedPriority)) + + attr := SchedAttr{SchedPolicy: SCHED_RR, SchedPriority: 60} + err := SchedSetAttr(pid, attr, 0) + require.NoError(t, err) + + attr, err = SchedGetAttr(pid) + require.NoError(t, err) + require.Equal(t, SCHED_RR, attr.SchedPolicy) + require.Equal(t, 60, int(attr.SchedPriority)) + } +}