Files
runc/internal/linux/linux.go
Kir Kolyshkin daf934fefc libct: use CPUSetDynamic for affinity and mempolicy masks
ToCPUSet was limited to unix.CPUSet (a fixed-size mask capped at 1024
CPUs). Switch CPUAffinity, LinuxMemoryPolicy.Nodes, ToCPUSet, and
SetMempolicy to unix.CPUSetDynamic, which can represent arbitrary CPU
and NUMA node IDs.

ToCPUSet now parses the input in a first pass to find the maximum value,
then allocates a mask large enough to hold it (out-of-range Set calls on
a dynamic mask are silently ignored). An arbitrary sanity cap is kept to
avoid huge allocations on bogus input.

This is backward compatible with reading older state.json: both the old
*unix.CPUSet (a fixed array) and the new CPUSetDynamic (a slice)
marshal to and from the same JSON array-of-numbers representation.

Update TestToCPUSet accordingly, adding coverage for values beyond the
old non-dynamic limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2026-07-27 17:12:24 -07:00

130 lines
3.4 KiB
Go

package linux
import (
"os"
"golang.org/x/sys/unix"
)
// Dup3 wraps [unix.Dup3].
func Dup3(oldfd, newfd, flags int) error {
err := retryOnEINTR(func() error {
return unix.Dup3(oldfd, newfd, flags)
})
return os.NewSyscallError("dup3", err)
}
// Exec wraps [unix.Exec].
func Exec(cmd string, args, env []string) error {
err := retryOnEINTR(func() error {
return unix.Exec(cmd, args, env)
})
if err != nil {
return &os.PathError{Op: "exec", Path: cmd, Err: err}
}
return nil
}
// Getwd wraps [unix.Getwd].
func Getwd() (wd string, err error) {
wd, err = retryOnEINTR2(unix.Getwd)
return wd, os.NewSyscallError("getwd", err)
}
// Open wraps [unix.Open].
func Open(path string, mode int, perm uint32) (fd int, err error) {
fd, err = retryOnEINTR2(func() (int, error) {
return unix.Open(path, mode, perm)
})
if err != nil {
return -1, &os.PathError{Op: "open", Path: path, Err: err}
}
return fd, nil
}
// Openat wraps [unix.Openat].
func Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
fd, err = retryOnEINTR2(func() (int, error) {
return unix.Openat(dirfd, path, mode, perm)
})
if err != nil {
return -1, &os.PathError{Op: "openat", Path: path, Err: err}
}
return fd, nil
}
// Recvfrom wraps [unix.Recvfrom].
func Recvfrom(fd int, p []byte, flags int) (n int, from unix.Sockaddr, err error) {
err = retryOnEINTR(func() error {
n, from, err = unix.Recvfrom(fd, p, flags)
return err
})
if err != nil {
return 0, nil, os.NewSyscallError("recvfrom", err)
}
return n, from, err
}
// SchedSetaffinity wraps [unix.SchedSetaffinityDynamic].
func SchedSetaffinity(pid int, aff unix.CPUSetDynamic) error {
err := retryOnEINTR(func() error {
return unix.SchedSetaffinityDynamic(pid, aff)
})
return os.NewSyscallError("sched_setaffinity", err)
}
// Sendmsg wraps [unix.Sendmsg].
func Sendmsg(fd int, p, oob []byte, to unix.Sockaddr, flags int) error {
err := retryOnEINTR(func() error {
return unix.Sendmsg(fd, p, oob, to, flags)
})
return os.NewSyscallError("sendmsg", err)
}
// SetMempolicy wraps [unix.SetMempolicyDynamic].
func SetMempolicy(mode int, mask unix.CPUSetDynamic) error {
err := retryOnEINTR(func() error {
return unix.SetMemPolicyDynamic(mode, mask)
})
return os.NewSyscallError("set_mempolicy", err)
}
// Readlinkat wraps [unix.Readlinkat].
func Readlinkat(dir *os.File, path string) (string, error) {
size := 4096
for {
linkBuf := make([]byte, size)
n, err := retryOnEINTR2(func() (int, error) {
return unix.Readlinkat(int(dir.Fd()), path, linkBuf)
})
if err != nil {
return "", &os.PathError{Op: "readlinkat", Path: dir.Name() + "/" + path, Err: err}
}
if n != size {
return string(linkBuf[:n]), nil
}
// Possible truncation, resize the buffer.
size *= 2
}
}
// GetPtyPeer is a wrapper for ioctl(TIOCGPTPEER).
func GetPtyPeer(ptyFd uintptr, unsafePeerPath string, flags int) (*os.File, error) {
// Make sure O_NOCTTY is always set -- otherwise runc might accidentally
// gain it as a controlling terminal. O_CLOEXEC also needs to be set to
// make sure we don't leak the handle either.
flags |= unix.O_NOCTTY | unix.O_CLOEXEC
// There is no nice wrapper for this kind of ioctl in unix.
peerFd, _, errno := unix.Syscall(
unix.SYS_IOCTL,
ptyFd,
uintptr(unix.TIOCGPTPEER),
uintptr(flags),
)
if errno != 0 {
return nil, os.NewSyscallError("ioctl TIOCGPTPEER", errno)
}
return os.NewFile(peerFd, unsafePeerPath), nil
}