-
Notifications
You must be signed in to change notification settings - Fork 31
add CONFIG_CGROUP_BPF to required kernel config #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -45,27 +45,72 @@ func (c *CgroupsValidator) Name() string { | |||||||||
|
||||||||||
const ( | ||||||||||
cgroupsConfigPrefix = "CGROUPS_" | ||||||||||
unifiedMountpoint = "/sys/fs/cgroup" | ||||||||||
mountsFilePath = "/proc/mounts" | ||||||||||
) | ||||||||||
|
||||||||||
// getUnifiedMountpoint checks if the default mount point is available. | ||||||||||
// If not, it parses the mounts file to find a valid cgroup mount point. | ||||||||||
func getUnifiedMountpoint(path string) (string, error) { | ||||||||||
f, err := os.Open(path) | ||||||||||
if err != nil { | ||||||||||
return "", err | ||||||||||
} | ||||||||||
defer f.Close() | ||||||||||
scanner := bufio.NewScanner(f) | ||||||||||
var cgroupV1MountPoint string | ||||||||||
for scanner.Scan() { | ||||||||||
line := scanner.Text() | ||||||||||
if !strings.Contains(line, "cgroup") { | ||||||||||
continue | ||||||||||
} | ||||||||||
// Example fields: `cgroup2 /sys/fs/cgroup cgroup2 rw,seclabel,nosuid,nodev,noexec,relatime 0 0`. | ||||||||||
fields := strings.Fields(line) | ||||||||||
if len(fields) >= 3 { | ||||||||||
switch fields[2] { | ||||||||||
case "cgroup2": | ||||||||||
// Return the first cgroups v2 mount point directly. | ||||||||||
return fields[1], nil | ||||||||||
case "cgroup": | ||||||||||
// Set the first cgroups v1 mount point only, | ||||||||||
// and continue the loop to find if there is a cgroups v2 mount point. | ||||||||||
if len(cgroupV1MountPoint) == 0 { | ||||||||||
cgroupV1MountPoint = fields[1] | ||||||||||
} | ||||||||||
Comment on lines
+76
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it ok to just do this?
Suggested change
i.e. always update the var if more cgroups v1 mount points are found or should we only catch the first one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For cgroup v1, this is an example. I prefer to get the first one. |
||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
// Return cgroups v1 mount point if no cgroups v2 mount point is found. | ||||||||||
if len(cgroupV1MountPoint) != 0 { | ||||||||||
return cgroupV1MountPoint, nil | ||||||||||
} | ||||||||||
return "", fmt.Errorf("cannot get a cgroupfs mount point from %q", path) | ||||||||||
} | ||||||||||
|
||||||||||
// Validate is part of the system.Validator interface. | ||||||||||
func (c *CgroupsValidator) Validate(spec SysSpec) (warns, errs []error) { | ||||||||||
// Get the subsystems from /sys/fs/cgroup/cgroup.controllers when cgroup v2 is used. | ||||||||||
// Get the subsystems from /sys/fs/cgroup/cgroup.controllers when cgroups v2 is used. | ||||||||||
// /proc/cgroups is meaningless for v2 | ||||||||||
// https://github.com/torvalds/linux/blob/v5.3/Documentation/admin-guide/cgroup-v2.rst#deprecated-v1-core-features | ||||||||||
var st unix.Statfs_t | ||||||||||
var err error | ||||||||||
unifiedMountpoint, err := getUnifiedMountpoint(mountsFilePath) | ||||||||||
if err != nil { | ||||||||||
return nil, []error{fmt.Errorf("cannot get a cgroup mount point: %w", err)} | ||||||||||
} | ||||||||||
if err := unix.Statfs(unifiedMountpoint, &st); err != nil { | ||||||||||
return nil, []error{fmt.Errorf("cannot statfs the cgroupv2 root: %w", err)} | ||||||||||
} | ||||||||||
var requiredCgroupSpec []string | ||||||||||
var optionalCgroupSpec []string | ||||||||||
var subsystems []string | ||||||||||
var warn error | ||||||||||
if st.Type == unix.CGROUP2_SUPER_MAGIC { | ||||||||||
subsystems, err = c.getCgroupV2Subsystems() | ||||||||||
subsystems, err, warn = c.getCgroupV2Subsystems(unifiedMountpoint) | ||||||||||
if err != nil { | ||||||||||
return nil, []error{fmt.Errorf("failed to get cgroups v2 subsystems: %w", err)} | ||||||||||
} | ||||||||||
if warn != nil { | ||||||||||
warns = append(warns, warn) | ||||||||||
} | ||||||||||
requiredCgroupSpec = spec.CgroupsV2 | ||||||||||
optionalCgroupSpec = spec.CgroupsV2Optional | ||||||||||
} else { | ||||||||||
|
@@ -110,11 +155,10 @@ func (c *CgroupsValidator) validateCgroupSubsystems(cgroups, subsystems []string | |||||||||
missing = append(missing, cgroup) | ||||||||||
} | ||||||||||
return missing | ||||||||||
|
||||||||||
} | ||||||||||
|
||||||||||
func (c *CgroupsValidator) getCgroupV1Subsystems() ([]string, error) { | ||||||||||
// Get the subsystems from /proc/cgroups when cgroup v1 is used. | ||||||||||
// Get the subsystems from /proc/cgroups when cgroups v1 is used. | ||||||||||
f, err := os.Open("/proc/cgroups") | ||||||||||
if err != nil { | ||||||||||
return nil, err | ||||||||||
|
@@ -138,19 +182,50 @@ func (c *CgroupsValidator) getCgroupV1Subsystems() ([]string, error) { | |||||||||
return subsystems, nil | ||||||||||
} | ||||||||||
|
||||||||||
func (c *CgroupsValidator) getCgroupV2Subsystems() ([]string, error) { | ||||||||||
func (c *CgroupsValidator) getCgroupV2Subsystems(unifiedMountpoint string) ([]string, error, error) { | ||||||||||
// Some controllers are implicitly enabled by the kernel. | ||||||||||
// Those controllers do not appear in /sys/fs/cgroup/cgroup.controllers. | ||||||||||
// https://github.com/torvalds/linux/blob/v5.3/kernel/cgroup/cgroup.c#L433-L434 | ||||||||||
// We assume these are always available, as it is hard to detect availability. | ||||||||||
// So, we hardcode the following as "pseudo" controllers. | ||||||||||
// - devices: implemented in kernel 4.15 | ||||||||||
// - freezer: implemented in kernel 5.2 | ||||||||||
pseudo := []string{"devices", "freezer"} | ||||||||||
// For freezer, we use checkCgroupV2Freeze() to check. | ||||||||||
// For others, we assume these are always available, as it is hard to detect availability. | ||||||||||
// We hardcode the following as initial controllers. | ||||||||||
// - devices: implemented in kernel 4.15. | ||||||||||
subsystems := []string{"devices"} | ||||||||||
freezeSupported, warn := checkCgroupV2Freeze(unifiedMountpoint) | ||||||||||
if freezeSupported { | ||||||||||
subsystems = append(subsystems, "freezer") | ||||||||||
} | ||||||||||
data, err := ioutil.ReadFile(filepath.Join(unifiedMountpoint, "cgroup.controllers")) | ||||||||||
if err != nil { | ||||||||||
return nil, err | ||||||||||
return nil, err, warn | ||||||||||
} | ||||||||||
subsystems := append(pseudo, strings.Fields(string(data))...) | ||||||||||
return subsystems, nil | ||||||||||
subsystems = append(subsystems, strings.Fields(string(data))...) | ||||||||||
return subsystems, err, warn | ||||||||||
} | ||||||||||
|
||||||||||
// checkCgroupV2Freeze checks if the freezer controller is enabled in Linux kernels 5.2. | ||||||||||
// It determines that by creating a cgroup.freeze file under the unified mountpoint location. | ||||||||||
func checkCgroupV2Freeze(unifiedMountpoint string) (isCgroupfs bool, warn error) { | ||||||||||
const freezeFile = "cgroup.freeze" | ||||||||||
tmpDir, warn := os.MkdirTemp(unifiedMountpoint, "freezer-test") | ||||||||||
pacoxu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
if warn != nil { | ||||||||||
return | ||||||||||
} | ||||||||||
defer func() { | ||||||||||
err := os.RemoveAll(tmpDir) | ||||||||||
if err != nil { | ||||||||||
warn = fmt.Errorf("error removing directory %q: %v", tmpDir, err) | ||||||||||
} | ||||||||||
}() | ||||||||||
_, warn = os.Stat(filepath.Join(tmpDir, freezeFile)) | ||||||||||
if os.IsNotExist(warn) { | ||||||||||
return | ||||||||||
} else if warn != nil { | ||||||||||
// If the err is not NotExist error, it means that `cgroup.freeze` exists. | ||||||||||
isCgroupfs = true | ||||||||||
warn = fmt.Errorf("could not stat %q file in %q: %v", freezeFile, tmpDir, warn) | ||||||||||
return | ||||||||||
} | ||||||||||
isCgroupfs = true | ||||||||||
return | ||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.