diff --git a/blkio.go b/blkio.go index 3f7dc91c..0e0534fb 100644 --- a/blkio.go +++ b/blkio.go @@ -20,7 +20,6 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" @@ -72,7 +71,7 @@ func (b *blkioController) Create(path string, resources *specs.LinuxResources) e } for _, t := range createBlkioSettings(resources.BlockIO) { if t.value != nil { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", t.name)), t.format(t.value), defaultFilePerm, diff --git a/cgroup.go b/cgroup.go index 263f5438..6c19f96e 100644 --- a/cgroup.go +++ b/cgroup.go @@ -18,7 +18,6 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -169,7 +168,7 @@ func (c *cgroup) add(process Process) error { if err != nil { return err } - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(s.Path(p), cgroupProcs), []byte(strconv.Itoa(process.Pid)), defaultFilePerm, @@ -199,7 +198,7 @@ func (c *cgroup) addTask(process Process) error { if err != nil { return err } - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(s.Path(p), cgroupTasks), []byte(strconv.Itoa(process.Pid)), defaultFilePerm, diff --git a/cpu.go b/cpu.go index ba8dda83..a7e530bf 100644 --- a/cpu.go +++ b/cpu.go @@ -19,7 +19,6 @@ package cgroups import ( "bufio" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -84,7 +83,7 @@ func (c *cpuController) Create(path string, resources *specs.LinuxResources) err value = []byte(strconv.FormatInt(*t.ivalue, 10)) } if value != nil { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(c.Path(path), fmt.Sprintf("cpu.%s", t.name)), value, defaultFilePerm, diff --git a/cpuset.go b/cpuset.go index fdf091bf..39573dcd 100644 --- a/cpuset.go +++ b/cpuset.go @@ -69,7 +69,7 @@ func (c *cpusetController) Create(path string, resources *specs.LinuxResources) }, } { if t.value != "" { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(c.Path(path), fmt.Sprintf("cpuset.%s", t.name)), []byte(t.value), defaultFilePerm, @@ -134,7 +134,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error { return err } if isEmpty(currentCpus) { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(current, "cpuset.cpus"), parentCpus, defaultFilePerm, @@ -143,7 +143,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error { } } if isEmpty(currentMems) { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(current, "cpuset.mems"), parentMems, defaultFilePerm, diff --git a/devices.go b/devices.go index f6a3b194..7792566d 100644 --- a/devices.go +++ b/devices.go @@ -18,7 +18,6 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -61,7 +60,7 @@ func (d *devicesController) Create(path string, resources *specs.LinuxResources) if device.Type == "" { device.Type = "a" } - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(d.Path(path), file), []byte(deviceString(device)), defaultFilePerm, diff --git a/freezer.go b/freezer.go index 5e668408..59a7e712 100644 --- a/freezer.go +++ b/freezer.go @@ -50,7 +50,7 @@ func (f *freezerController) Thaw(path string) error { } func (f *freezerController) changeState(path string, state State) error { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(f.root, path, "freezer.state"), []byte(strings.ToUpper(string(state))), defaultFilePerm, diff --git a/hugetlb.go b/hugetlb.go index e5def581..c0eb03b2 100644 --- a/hugetlb.go +++ b/hugetlb.go @@ -17,7 +17,6 @@ package cgroups import ( - "io/ioutil" "os" "path/filepath" "strconv" @@ -57,7 +56,7 @@ func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) return err } for _, limit := range resources.HugepageLimits { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")), []byte(strconv.FormatUint(limit.Limit, 10)), defaultFilePerm, diff --git a/memory.go b/memory.go index 74ad3714..3f5c3c03 100644 --- a/memory.go +++ b/memory.go @@ -20,7 +20,6 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" @@ -214,7 +213,7 @@ func (m *memoryController) Create(path string, resources *specs.LinuxResources) // until a limit is set on the cgroup and limit cannot be set once the // cgroup has children, or if there are already tasks in the cgroup. for _, i := range []int64{1, -1} { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(m.Path(path), "memory.kmem.limit_in_bytes"), []byte(strconv.FormatInt(i, 10)), defaultFilePerm, @@ -378,7 +377,7 @@ func (m *memoryController) parseStats(r io.Reader, stat *v1.MemoryStat) error { func (m *memoryController) set(path string, settings []memorySettings) error { for _, t := range settings { if t.value != nil { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(m.Path(path), fmt.Sprintf("memory.%s", t.name)), []byte(strconv.FormatInt(*t.value, 10)), defaultFilePerm, @@ -468,7 +467,7 @@ func (m *memoryController) memoryEvent(path string, event MemoryEvent) (uintptr, defer evtFile.Close() data := fmt.Sprintf("%d %d %s", efd, evtFile.Fd(), event.Arg()) evctlPath := filepath.Join(root, "cgroup.event_control") - if err := ioutil.WriteFile(evctlPath, []byte(data), 0700); err != nil { + if err := retryingWriteFile(evctlPath, []byte(data), 0700); err != nil { unix.Close(efd) return 0, err } diff --git a/net_cls.go b/net_cls.go index 8f1a2651..28882575 100644 --- a/net_cls.go +++ b/net_cls.go @@ -17,7 +17,6 @@ package cgroups import ( - "io/ioutil" "os" "path/filepath" "strconv" @@ -48,7 +47,7 @@ func (n *netclsController) Create(path string, resources *specs.LinuxResources) return err } if resources.Network != nil && resources.Network.ClassID != nil && *resources.Network.ClassID > 0 { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(n.Path(path), "net_cls.classid"), []byte(strconv.FormatUint(uint64(*resources.Network.ClassID), 10)), defaultFilePerm, diff --git a/net_prio.go b/net_prio.go index 612e1bcd..6362fd08 100644 --- a/net_prio.go +++ b/net_prio.go @@ -18,7 +18,6 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -49,7 +48,7 @@ func (n *netprioController) Create(path string, resources *specs.LinuxResources) } if resources.Network != nil { for _, prio := range resources.Network.Priorities { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(n.Path(path), "net_prio.ifpriomap"), formatPrio(prio.Name, prio.Priority), defaultFilePerm, diff --git a/pids.go b/pids.go index 6297f249..ce78e44c 100644 --- a/pids.go +++ b/pids.go @@ -50,7 +50,7 @@ func (p *pidsController) Create(path string, resources *specs.LinuxResources) er return err } if resources.Pids != nil && resources.Pids.Limit > 0 { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(p.Path(path), "pids.max"), []byte(strconv.FormatInt(resources.Pids.Limit, 10)), defaultFilePerm, diff --git a/rdma.go b/rdma.go index f5085aa6..b6f0d416 100644 --- a/rdma.go +++ b/rdma.go @@ -67,7 +67,7 @@ func (p *rdmaController) Create(path string, resources *specs.LinuxResources) er for device, limit := range resources.Rdma { if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(p.Path(path), "rdma.max"), []byte(createCmdString(device, &limit)), defaultFilePerm, diff --git a/utils.go b/utils.go index 7ba1cd9b..f52df59e 100644 --- a/utils.go +++ b/utils.go @@ -18,6 +18,7 @@ package cgroups import ( "bufio" + "errors" "fmt" "io" "io/ioutil" @@ -26,6 +27,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" units "github.com/docker/go-units" @@ -382,3 +384,16 @@ func cleanPath(path string) string { } return filepath.Clean(path) } + +func retryingWriteFile(path string, data []byte, mode os.FileMode) error { + // Retry writes on EINTR; see: + // https://github.com/golang/go/issues/38033 + for { + err := ioutil.WriteFile(path, []byte(data), mode) + if err == nil { + return nil + } else if !errors.Is(err, syscall.EINTR) { + return err + } + } +} diff --git a/v2/manager.go b/v2/manager.go index b1ec69ba..ac1b06e0 100644 --- a/v2/manager.go +++ b/v2/manager.go @@ -18,6 +18,7 @@ package v2 import ( "bufio" + stderrors "errors" "fmt" "io/ioutil" "math" @@ -149,11 +150,21 @@ func (c *Value) write(path string, perm os.FileMode) error { default: return ErrInvalidFormat } - return ioutil.WriteFile( - filepath.Join(path, c.filename), - data, - perm, - ) + + // Retry writes on EINTR; see: + // https://github.com/golang/go/issues/38033 + for { + err := ioutil.WriteFile( + filepath.Join(path, c.filename), + data, + perm, + ) + if err == nil { + return nil + } else if !stderrors.Is(err, syscall.EINTR) { + return err + } + } } func writeValues(path string, values []Value) error {