File: utils_linux.go

package info (click to toggle)
golang-github-containers-common 0.56.0%2Bds1-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,852 kB
  • sloc: makefile: 126; sh: 62
file content (145 lines) | stat: -rw-r--r-- 4,106 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//go:build linux
// +build linux

package cgroups

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

// WriteFile writes to a cgroup file
func WriteFile(dir, file, data string) error {
	fd, err := OpenFile(dir, file, unix.O_WRONLY)
	if err != nil {
		return err
	}
	defer fd.Close()
	for {
		_, err := fd.WriteString(data)
		if errors.Is(err, unix.EINTR) {
			logrus.Infof("interrupted while writing %s to %s", data, fd.Name())
			continue
		}
		return err
	}
}

// OpenFile opens a cgroup file with the given flags
func OpenFile(dir, file string, flags int) (*os.File, error) {
	var resolveFlags uint64
	mode := os.FileMode(0)
	if TestMode && flags&os.O_WRONLY != 0 {
		flags |= os.O_TRUNC | os.O_CREATE
		mode = 0o600
	}
	cgroupPath := path.Join(dir, file)
	relPath := strings.TrimPrefix(cgroupPath, cgroupRoot+"/")

	var stats unix.Statfs_t
	fdTest, errOpen := unix.Openat2(-1, cgroupRoot, &unix.OpenHow{
		Flags: unix.O_DIRECTORY | unix.O_PATH,
	})
	errStat := unix.Fstatfs(fdTest, &stats)
	cgroupFd := fdTest

	resolveFlags = unix.RESOLVE_BENEATH | unix.RESOLVE_NO_MAGICLINKS
	if stats.Type == unix.CGROUP2_SUPER_MAGIC {
		// cgroupv2 has a single mountpoint and no "cpu,cpuacct" symlinks
		resolveFlags |= unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_SYMLINKS
	}

	if errOpen != nil || errStat != nil || (len(relPath) == len(cgroupPath)) { // openat2 not available, use os
		fdTest, err := os.OpenFile(cgroupPath, flags, mode)
		if err != nil {
			return nil, err
		}
		if TestMode {
			return fdTest, nil
		}
		if err := unix.Fstatfs(int(fdTest.Fd()), &stats); err != nil {
			_ = fdTest.Close()
			return nil, &os.PathError{Op: "statfs", Path: cgroupPath, Err: err}
		}
		if stats.Type != unix.CGROUP_SUPER_MAGIC && stats.Type != unix.CGROUP2_SUPER_MAGIC {
			_ = fdTest.Close()
			return nil, &os.PathError{Op: "open", Path: cgroupPath, Err: errors.New("not a cgroup file")}
		}
		return fdTest, nil
	}

	fd, err := unix.Openat2(cgroupFd, relPath,
		&unix.OpenHow{
			Resolve: resolveFlags,
			Flags:   uint64(flags) | unix.O_CLOEXEC,
			Mode:    uint64(mode),
		})
	if err != nil {
		return nil, err
	}

	return os.NewFile(uintptr(fd), cgroupPath), nil
}

// ReadFile reads from a cgroup file, opening it with the read only flag
func ReadFile(dir, file string) (string, error) {
	fd, err := OpenFile(dir, file, unix.O_RDONLY)
	if err != nil {
		return "", err
	}
	defer fd.Close()
	var buf bytes.Buffer

	_, err = buf.ReadFrom(fd)
	return buf.String(), err
}

// GetBlkioFiles gets the proper files for blkio weights
func GetBlkioFiles(cgroupPath string) (wtFile, wtDevFile string) {
	var weightFile string
	var weightDeviceFile string
	// in this important since runc keeps these variables private, they won't be set
	if cgroups.PathExists(filepath.Join(cgroupPath, "blkio.weight")) {
		weightFile = "blkio.weight"
		weightDeviceFile = "blkio.weight_device"
	} else {
		weightFile = "blkio.bfq.weight"
		weightDeviceFile = "blkio.bfq.weight_device"
	}
	return weightFile, weightDeviceFile
}

// SetBlkioThrottle sets the throttle limits for the cgroup
func SetBlkioThrottle(res *configs.Resources, cgroupPath string) error {
	for _, td := range res.BlkioThrottleReadBpsDevice {
		if err := WriteFile(cgroupPath, "blkio.throttle.read_bps_device", fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate)); err != nil {
			return err
		}
	}
	for _, td := range res.BlkioThrottleWriteBpsDevice {
		if err := WriteFile(cgroupPath, "blkio.throttle.write_bps_device", fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate)); err != nil {
			return err
		}
	}
	for _, td := range res.BlkioThrottleReadIOPSDevice {
		if err := WriteFile(cgroupPath, "blkio.throttle.read_iops_device", td.String()); err != nil {
			return err
		}
	}
	for _, td := range res.BlkioThrottleWriteIOPSDevice {
		if err := WriteFile(cgroupPath, "blkio.throttle.write_iops_device", td.String()); err != nil {
			return err
		}
	}
	return nil
}