From f34eb2c003f2f7c1835222d136d02b19761a7ae5 Mon Sep 17 00:00:00 2001 From: Danail Branekov Date: Wed, 18 Mar 2020 13:00:05 +0200 Subject: [PATCH 1/2] Retry writing to cgroup files on EINTR error Golang 1.14 introduces asynchronous preemption which results into applications getting frequent EINTR (syscall interrupted) errors when invoking slow syscalls, e.g. when writing to cgroup files. As writing to cgroups is idempotent, it is safe to retry writing to the file whenever the write syscall is interrupted. Signed-off-by: Mario Nitchev --- libcontainer/cgroups/fscommon/fscommon.go | 26 +++++++++++++- .../cgroups/fscommon/fscommon_test.go | 35 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 libcontainer/cgroups/fscommon/fscommon_test.go diff --git a/libcontainer/cgroups/fscommon/fscommon.go b/libcontainer/cgroups/fscommon/fscommon.go index dd92e8c8..dc53987e 100644 --- a/libcontainer/cgroups/fscommon/fscommon.go +++ b/libcontainer/cgroups/fscommon/fscommon.go @@ -4,9 +4,12 @@ package fscommon import ( "io/ioutil" + "os" + "syscall" securejoin "github.com/cyphar/filepath-securejoin" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func WriteFile(dir, file, data string) error { @@ -17,7 +20,7 @@ func WriteFile(dir, file, data string) error { if err != nil { return err } - if err := ioutil.WriteFile(path, []byte(data), 0700); err != nil { + if err := retryingWriteFile(path, []byte(data), 0700); err != nil { return errors.Wrapf(err, "failed to write %q to %q", data, path) } return nil @@ -34,3 +37,24 @@ func ReadFile(dir, file string) (string, error) { data, err := ioutil.ReadFile(path) return string(data), err } + +func retryingWriteFile(filename string, data []byte, perm os.FileMode) error { + for { + err := ioutil.WriteFile(filename, data, perm) + if isInterruptedWriteFile(err) { + logrus.Infof("interrupted while writing %s to %s", string(data), filename) + continue + } + return err + } +} + +func isInterruptedWriteFile(err error) bool { + if patherr, ok := err.(*os.PathError); ok { + errno, ok2 := patherr.Err.(syscall.Errno) + if ok2 && errno == syscall.EINTR { + return true + } + } + return false +} diff --git a/libcontainer/cgroups/fscommon/fscommon_test.go b/libcontainer/cgroups/fscommon/fscommon_test.go new file mode 100644 index 00000000..6d6f8bbe --- /dev/null +++ b/libcontainer/cgroups/fscommon/fscommon_test.go @@ -0,0 +1,35 @@ +// +build linux + +package fscommon + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "testing" + "time" + + "github.com/opencontainers/runc/libcontainer/cgroups" +) + +func TestWriteCgroupFileHandlesInterrupt(t *testing.T) { + memoryCgroupMount, err := cgroups.FindCgroupMountpoint("", "memory") + if err != nil { + t.Fatal(err) + } + + cgroupName := fmt.Sprintf("test-eint-%d", time.Now().Nanosecond()) + cgroupPath := filepath.Join(memoryCgroupMount, cgroupName) + if err := os.MkdirAll(cgroupPath, 0755); err != nil { + t.Fatal(err) + } + defer os.RemoveAll(cgroupPath) + + for i := 0; i < 100000; i++ { + limit := 1024*1024 + i + if err := WriteFile(cgroupPath, "memory.limit_in_bytes", strconv.Itoa(limit)); err != nil { + t.Fatalf("Failed to write %d on attempt %d: %+v", limit, i, err) + } + } +} From 648295be98e9c3c3b4620507e939dbe69083955a Mon Sep 17 00:00:00 2001 From: Mario Nitchev Date: Thu, 19 Mar 2020 12:52:08 +0200 Subject: [PATCH 2/2] Skip test for cgroups v2 Signed-off-by: Yulia Nedyalkova --- libcontainer/cgroups/fscommon/fscommon_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libcontainer/cgroups/fscommon/fscommon_test.go b/libcontainer/cgroups/fscommon/fscommon_test.go index 6d6f8bbe..e56052e0 100644 --- a/libcontainer/cgroups/fscommon/fscommon_test.go +++ b/libcontainer/cgroups/fscommon/fscommon_test.go @@ -14,6 +14,10 @@ import ( ) func TestWriteCgroupFileHandlesInterrupt(t *testing.T) { + if cgroups.IsCgroup2UnifiedMode() { + t.Skip("cgroup v2 is not supported") + } + memoryCgroupMount, err := cgroups.FindCgroupMountpoint("", "memory") if err != nil { t.Fatal(err)