mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
runc exec: implement CPU affinity
As per - https://github.com/opencontainers/runtime-spec/pull/1253 - https://github.com/opencontainers/runtime-spec/pull/1261 CPU affinity can be set in two ways: 1. When creating/starting a container, in config.json's Process.ExecCPUAffinity, which is when applied to all execs. 2. When running an exec, in process.json's CPUAffinity, which applied to a given exec and overrides the value from (1). Add some basic tests. Note that older kernels (RHEL8, Ubuntu 20.04) change CPU affinity of a process to that of a container's cgroup, as soon as it is moved to that cgroup, while newer kernels (Ubuntu 24.04, Fedora 41) don't do that. Because of the above, - it's impossible to really test initial CPU affinity without adding debug logging to libcontainer/nsenter; - for older kernels, there can be a brief moment when exec's affinity is different than either initial or final affinity being set; - exec's final CPU affinity, if not specified, can be different depending on the kernel, therefore we don't test it. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
@@ -163,13 +163,52 @@ type setnsProcess struct {
|
||||
initProcessPid int
|
||||
}
|
||||
|
||||
// Starts setns process with specified initial CPU affinity.
|
||||
func (p *setnsProcess) startWithCPUAffinity() error {
|
||||
aff := p.config.CPUAffinity
|
||||
if aff == nil || aff.Initial == nil {
|
||||
return p.cmd.Start()
|
||||
}
|
||||
errCh := make(chan error)
|
||||
defer close(errCh)
|
||||
|
||||
// Use a goroutine to dedicate an OS thread.
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
// Command inherits the CPU affinity.
|
||||
if err := unix.SchedSetaffinity(unix.Gettid(), aff.Initial); err != nil {
|
||||
errCh <- fmt.Errorf("error setting initial CPU affinity: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
errCh <- p.cmd.Start()
|
||||
// Deliberately omit runtime.UnlockOSThread here.
|
||||
// https://pkg.go.dev/runtime#LockOSThread says:
|
||||
// "If the calling goroutine exits without unlocking the
|
||||
// thread, the thread will be terminated".
|
||||
}()
|
||||
|
||||
return <-errCh
|
||||
}
|
||||
|
||||
func (p *setnsProcess) setFinalCPUAffinity() error {
|
||||
aff := p.config.CPUAffinity
|
||||
if aff == nil || aff.Final == nil {
|
||||
return nil
|
||||
}
|
||||
if err := unix.SchedSetaffinity(p.pid(), aff.Final); err != nil {
|
||||
return fmt.Errorf("error setting final CPU affinity: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *setnsProcess) start() (retErr error) {
|
||||
defer p.comm.closeParent()
|
||||
|
||||
// get the "before" value of oom kill count
|
||||
// Get the "before" value of oom kill count.
|
||||
oom, _ := p.manager.OOMKillCount()
|
||||
err := p.cmd.Start()
|
||||
// close the child-side of the pipes (controlled by child)
|
||||
err := p.startWithCPUAffinity()
|
||||
// Close the child-side of the pipes (controlled by child).
|
||||
p.comm.closeChild()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting setns process: %w", err)
|
||||
@@ -219,6 +258,10 @@ func (p *setnsProcess) start() (retErr error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set final CPU affinity right after the process is moved into container's cgroup.
|
||||
if err := p.setFinalCPUAffinity(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.intelRdtPath != "" {
|
||||
// if Intel RDT "resource control" filesystem path exists
|
||||
_, err := os.Stat(p.intelRdtPath)
|
||||
|
||||
Reference in New Issue
Block a user