Skip to content

Commit

Permalink
runc kill: add support for cgroup.kill
Browse files Browse the repository at this point in the history
cgroup.kill inteface was added to Linux kernel 5.14 (see [1], [2]).
Use it if we can.

[1] https://lwn.net/Articles/855049/
[2] https://lwn.net/Articles/855924/

Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin committed Apr 27, 2023
1 parent bbdd7f1 commit e861f49
Showing 1 changed file with 92 additions and 15 deletions.
107 changes: 92 additions & 15 deletions libcontainer/signal_all_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package libcontainer

import (
"errors"
"fmt"
"os"
"unsafe"

Expand Down Expand Up @@ -34,36 +35,57 @@ func isWaitable(pid int) (bool, error) {
return si.pid != 0, nil
}

// signalAllProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending the signal s to them.
// If s is SIGKILL and subreaper is not enabled then it will wait for each
// process to exit.
// For all other signals it will check if the process is ready to report its
// exit status and only if it is will a wait be performed.
// signalAllProcesses sends signal to all the process inside the manager's cgroup.
// In case the signal is SIGKILL, and cgroup.kill is available, it is used. Otherwise,
// the cgroup is frozen, then the signal is sent to all the processes one by one.
//
// If s is SIGKILL and subreaper is not enabled, this function waits for each
// process to exit. For all other signals it will check if the process is ready
// to report its exit status and only if it is will a wait be performed.
func signalAllProcesses(m cgroups.Manager, s os.Signal) error {
sig, ok := s.(unix.Signal)
if !ok {
return errors.New("unsupported signal type")
}

if err := m.Freeze(configs.Frozen); err != nil {
logrus.Warn(err)
haveCgroupKill := false

// Use cgroup.kill, if available.
if s == unix.SIGKILL {
if p := m.Path(""); p != "" { // Either cgroup v2 or hybrid.
if err := cgroupKillAll(p); err == nil {
haveCgroupKill = true
} else if !errors.Is(err, unix.ENOENT) {
logrus.Warnf("cgroupKillAll: %v", err)
}
}
}

if !haveCgroupKill {
if err := m.Freeze(configs.Frozen); err != nil {
logrus.Warn(err)
}
}

pids, err := m.GetAllPids()
if err != nil {
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
if !haveCgroupKill {
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
}
}
return err
}
for _, pid := range pids {
if err := unix.Kill(pid, sig); err != nil && err != unix.ESRCH {
if !haveCgroupKill {
for _, pid := range pids {
if err := unix.Kill(pid, sig); err != nil && err != unix.ESRCH {
logrus.Warn(err)
}
}
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
}
}
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
}

subreaper, err := system.GetSubreaper()
if err != nil {
Expand Down Expand Up @@ -103,3 +125,58 @@ func signalAllProcesses(m cgroups.Manager, s os.Signal) error {
}
return nil
}

func prepareCgWait(dir string) (int, error) {
fd, err := unix.InotifyInit()
if err != nil {
return -1, fmt.Errorf("unable to init inotify: %w", err)
}
_, err = unix.InotifyAddWatch(fd, dir+"/cgroup.events", unix.IN_MODIFY)
if err != nil {
unix.Close(fd)
return -1, fmt.Errorf("unable to add inotify watch: %w", err)
}
return fd, nil
}

func cgWait(fd int) error {
fds := []unix.PollFd{{
Fd: int32(fd),
Events: unix.POLLIN,
}}
for {
res, err := unix.Poll(fds, 10000)
if err == unix.EINTR {
continue
}
if err != nil {
return &os.SyscallError{Syscall: "poll", Err: err}
}
if res == 0 { // Timeout.
return &os.SyscallError{Syscall: "poll", Err: unix.ETIMEDOUT}
}
if res > 0 && fds[0].Revents&unix.POLLIN != 0 {
return nil
}
}
}

func cgroupKillAll(path string) error {
const file = "cgroup.kill"
if err := unix.Access(path+"/"+file, unix.F_OK); err != nil {
return &os.PathError{Op: "access", Path: path + "/" + file, Err: err}
}

fd, err := prepareCgWait(path)
if err != nil {
return err
}

err = cgroups.WriteFile(path, file, "1")
if err == nil {
err = cgWait(fd)
}
_ = unix.Close(fd)

return err
}

0 comments on commit e861f49

Please sign in to comment.