Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rlimit cache rework for Go 1.23+ #4290

Merged
merged 1 commit into from
Jun 1, 2024
Merged
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
16 changes: 9 additions & 7 deletions libcontainer/init_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"runtime/debug"
"strconv"
"strings"
"syscall"

"github.com/containerd/console"
"github.com/moby/sys/user"
Expand Down Expand Up @@ -225,9 +226,7 @@ func containerInit(t initType, config *initConfig, pipe *syncSocket, consoleSock

// Clean the RLIMIT_NOFILE cache in go runtime.
// Issue: https://github.com/opencontainers/runc/issues/4195
if containsRlimit(config.Rlimits, unix.RLIMIT_NOFILE) {
system.ClearRlimitNofileCache()
}
maybeClearRlimitNofileCache(config.Rlimits)

switch t {
case initSetns:
Expand Down Expand Up @@ -655,13 +654,16 @@ func setupRoute(config *configs.Config) error {
return nil
}

func containsRlimit(limits []configs.Rlimit, resource int) bool {
func maybeClearRlimitNofileCache(limits []configs.Rlimit) {
for _, rlimit := range limits {
if rlimit.Type == resource {
return true
if rlimit.Type == syscall.RLIMIT_NOFILE {
system.ClearRlimitNofileCache(&syscall.Rlimit{
Cur: rlimit.Soft,
Max: rlimit.Hard,
})
return
}
}
return false
}

func setupRlimits(limits []configs.Rlimit, pid int) error {
Expand Down
16 changes: 0 additions & 16 deletions libcontainer/system/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,13 @@ import (
"io"
"os"
"strconv"
"sync/atomic"
"syscall"
"unsafe"

"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile
var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit]

// ClearRlimitNofileCache is to clear go runtime's nofile rlimit cache.
func ClearRlimitNofileCache() {
// As reported in issue #4195, the new version of go runtime(since 1.19)
// will cache rlimit-nofile. Before executing execve, the rlimit-nofile
// of the process will be restored with the cache. In runc, this will
// cause the rlimit-nofile setting by the parent process for the container
// to become invalid. It can be solved by clearing this cache. But
// unfortunately, go stdlib doesn't provide such function, so we need to
// link to the private var `origRlimitNofile` in package syscall to hack.
syscallOrigRlimitNofile.Store(nil)
}

type ParentDeathSignal int

func (p ParentDeathSignal) Restore() error {
Expand Down
15 changes: 15 additions & 0 deletions libcontainer/system/rlimit_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build go1.23

package system

import (
"syscall"
)

// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. The argument
// is process RLIMIT_NOFILE values. Relies on go.dev/cl/588076.
func ClearRlimitNofileCache(lim *syscall.Rlimit) {
// Ignore the return values since we only need to clean the cache,
// the limit is going to be set via unix.Prlimit elsewhere.
_ = syscall.Setrlimit(syscall.RLIMIT_NOFILE, lim)
}
27 changes: 27 additions & 0 deletions libcontainer/system/rlimit_linux_go122.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build !go1.23

// TODO: remove this file once go 1.22 is no longer supported.

package system

import (
"sync/atomic"
"syscall"
_ "unsafe" // Needed for go:linkname to work.
)

//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile
var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit]

// ClearRlimitNofileCache clears go runtime's nofile rlimit cache.
kolyshkin marked this conversation as resolved.
Show resolved Hide resolved
// The argument is process RLIMIT_NOFILE values.
func ClearRlimitNofileCache(_ *syscall.Rlimit) {
// As reported in issue #4195, the new version of go runtime(since 1.19)
// will cache rlimit-nofile. Before executing execve, the rlimit-nofile
// of the process will be restored with the cache. In runc, this will
// cause the rlimit-nofile setting by the parent process for the container
// to become invalid. It can be solved by clearing this cache. But
// unfortunately, go stdlib doesn't provide such function, so we need to
// link to the private var `origRlimitNofile` in package syscall to hack.
syscallOrigRlimitNofile.Store(nil)
}
Loading