Handle potential wait4 EINTR & add memory cgroup

This commit is contained in:
criyle 2020-03-01 01:34:40 -05:00
parent 53a90d3a09
commit cf5ebfb3ef
8 changed files with 64 additions and 38 deletions

View File

@ -361,9 +361,13 @@ func start() (*types.Result, error) {
if err != nil {
return nil, fmt.Errorf("cgroup memory: %v", err)
}
debug("cgroup: cpu: ", cpu, " memory: ", memory)
cache, err := cg.FindMemoryStatProperty("cache")
if err != nil {
return nil, fmt.Errorf("cgroup cache %v", err)
}
debug("cgroup: cpu: ", cpu, " memory: ", memory, "cache: ", cache)
rt.Time = time.Duration(cpu)
rt.Memory = types.Size(memory)
rt.Memory = types.Size(memory - cache)
debug("cgroup:", rt)
}
return &rt, nil

View File

@ -97,7 +97,7 @@ func (c *containerServer) handleExecve(cmd *execCmd, msg *unixsocket.Msg) error
<-waitDone
// collect zombies
for {
if pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); err != syscall.EINTR || pid <= 0 {
if _, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); err != nil && err != syscall.EINTR {
break
}
}
@ -108,6 +108,9 @@ func (c *containerServer) handleExecve(cmd *execCmd, msg *unixsocket.Msg) error
var rusage syscall.Rusage
if err == nil {
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
for err == syscall.EINTR {
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
}
}
// sync with kill goroutine
close(waitDone)

2
go.mod
View File

@ -4,5 +4,5 @@ go 1.14
require (
github.com/seccomp/libseccomp-golang v0.9.1
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
golang.org/x/sys v0.0.0-20200301040627-c5d0d7b4ec88
)

4
go.sum
View File

@ -1,4 +1,4 @@
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200301040627-c5d0d7b4ec88 h1:LNVdAhESTW4gWDhYvciNcGoS9CEcxRiUKE9kSgw+X3s=
golang.org/x/sys v0.0.0-20200301040627-c5d0d7b4ec88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -1,6 +1,10 @@
package cgroup
import "os"
import (
"bytes"
"fmt"
"os"
)
// additional ideas:
// cpu share(not used): cpu.share
@ -110,6 +114,26 @@ func (c *CGroup) SetMemoryMaxUsageInBytes(i uint64) error {
return c.memory.WriteUint("memory.max_usage_in_bytes", i)
}
// FindMemoryStatProperty find certain property from memory.stat
func (c *CGroup) FindMemoryStatProperty(prop string) (uint64, error) {
content, err := c.memory.ReadFile("memory.stat")
if err != nil {
return 0, err
}
r := bytes.NewReader(content)
for {
var p string
var i uint64
_, err = fmt.Fscanln(r, &p, &i)
if err != nil {
return 0, err
}
if p == prop {
return i, nil
}
}
}
func remove(name string) error {
if name != "" {
return os.Remove(name)

View File

@ -2,6 +2,7 @@ package cgroup
import (
"errors"
"io/ioutil"
"path"
"strconv"
"strings"
@ -28,7 +29,7 @@ func (c *SubCGroup) WriteUint(filename string, i uint64) error {
if c.path == "" {
return nil
}
return writeFileNoInterrupt(path.Join(c.path, filename), []byte(strconv.FormatUint(i, 10)))
return c.WriteFile(filename, []byte(strconv.FormatUint(i, 10)))
}
// ReadUint read uint64 into given file
@ -36,7 +37,7 @@ func (c *SubCGroup) ReadUint(filename string) (uint64, error) {
if c.path == "" {
return 0, ErrNotInitialized
}
b, err := readFileNoInterrupt(path.Join(c.path, filename))
b, err := c.ReadFile(filename)
if err != nil {
return 0, err
}
@ -47,37 +48,24 @@ func (c *SubCGroup) ReadUint(filename string) (uint64, error) {
return s, nil
}
// writeFileNoInterrupt handles potential EINTR error while writes to
// WriteFile writes cgroup file and handles potential EINTR error while writes to
// the slow device (cgroup)
func writeFileNoInterrupt(path string, content []byte) error {
fd, err := syscall.Open(path, syscall.O_WRONLY|syscall.O_TRUNC|syscall.O_CLOEXEC, 0664)
if err != nil {
return err
func (c *SubCGroup) WriteFile(name string, content []byte) error {
p := path.Join(c.path, name)
err := ioutil.WriteFile(p, content, 0664)
for err != nil && errors.Is(err, syscall.EINTR) {
err = ioutil.WriteFile(p, content, 0664)
}
defer syscall.Close(fd)
_, err = syscall.Write(fd, content)
for err == syscall.EINTR {
_, err = syscall.Write(fd, content)
}
return nil
return err
}
const maxUintFile = 64 // max file size 64 bytes (enough for uint)
// readFileNoInterrupt handles potential EINTR error while read to
// ReadFile reads cgroup file and handles potential EINTR error while read to
// the slow device (cgroup)
func readFileNoInterrupt(path string) ([]byte, error) {
fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_CLOEXEC, 0664)
if err != nil {
return nil, err
func (c *SubCGroup) ReadFile(name string) ([]byte, error) {
p := path.Join(c.path, name)
data, err := ioutil.ReadFile(p)
for err != nil && errors.Is(err, syscall.EINTR) {
data, err = ioutil.ReadFile(p)
}
defer syscall.Close(fd)
buff := make([]byte, maxUintFile)
n, err := syscall.Read(fd, buff)
for err == syscall.EINTR {
n, err = syscall.Read(fd, buff)
}
return buff[:n], nil
return data, err
}

View File

@ -82,6 +82,10 @@ func (t *Tracer) TraceRun(c context.Context) (result types.Result) {
// Ensure the process have called setpgid
pid, err = unix.Wait4(pgid, &wstatus, unix.WALL, &rusage)
}
if err == unix.EINTR {
t.Handler.Debug("wait4 EINTR")
continue
}
if err != nil {
t.Handler.Debug("wait4 failed: ", err)
result.Status = types.StatusRunnerError
@ -288,7 +292,7 @@ func collectZombie(pgid int) {
var wstatus unix.WaitStatus
// collect zombies
for {
if _, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOHANG, nil); err != nil {
if _, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOHANG, nil); err != unix.EINTR && err != nil {
break
}
}

View File

@ -86,6 +86,9 @@ func (r *Runner) trace(c context.Context, runner *forkexec.Runner) (result types
fTime = time.Now()
for {
_, err := unix.Wait4(pgid, &wstatus, 0, &rusage)
if err == unix.EINTR {
continue
}
r.println("wait4: ", wstatus)
if err != nil {
result.Status = types.StatusRunnerError
@ -150,7 +153,7 @@ func killAll(pgid int) {
func collectZombie(pgid int) {
var wstatus unix.WaitStatus
for {
if _, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOHANG, nil); err != nil {
if _, err := unix.Wait4(-pgid, &wstatus, unix.WALL|unix.WNOHANG, nil); err != unix.EINTR && err != nil {
break
}
}