diff --git a/internal/pkg/sysinfo/probes/linux/cgroup_v2.go b/internal/pkg/sysinfo/probes/linux/cgroup_v2.go index 9b2d9e8e5942..b1f78e7260b1 100644 --- a/internal/pkg/sysinfo/probes/linux/cgroup_v2.go +++ b/internal/pkg/sysinfo/probes/linux/cgroup_v2.go @@ -22,6 +22,8 @@ package linux import ( "errors" "fmt" + "os" + "path/filepath" "regexp" "strconv" @@ -43,26 +45,66 @@ func (g *cgroupV2) probeController(controllerName string) (cgroupControllerAvail } func (g *cgroupV2) loadControllers(seen func(string, string)) error { - // Some controllers are implicitly enabled by the kernel. Those controllers - // do not appear in the cgroup.controllers file. Their availability is - // assumed based on the kernel version, as it is hard to detect them - // directly. - // https://github.com/torvalds/linux/blob/v5.3/kernel/cgroup/cgroup.c#L433-L434 + // The device controller has no interface files. Its availability is assumed + // based on the kernel version, as it is hard to detect it directly. + // https://github.com/torvalds/linux/blob/v5.3/Documentation/admin-guide/cgroup-v2.rst#device-controller if major, minor, err := parseKernelRelease(g.probeUname); err == nil { /* devices: since 4.15 */ if major > 4 || (major == 4 && minor >= 15) { seen("devices", "assumed") } - /* freezer: since 5.2 */ if major > 5 || (major == 5 && minor >= 2) { - seen("freezer", "assumed") - } } else { return err } - if err := g.detectListedRootControllers(seen); err != nil { - return err + return errors.Join( + g.detectFreezerController(seen), + g.detectListedRootControllers(seen), + ) +} + +// Detect the freezer controller. It doesn't appear in the cgroup.controllers +// file. Check for the existence of the cgroup.freeze file in the k0s cgroup +// instead, or try to create a dummy cgroup if k0s runs in the root cgroup. +// +// https://github.com/torvalds/linux/blob/v5.3/Documentation/admin-guide/cgroup-v2.rst#core-interface-files +func (g *cgroupV2) detectFreezerController(seen func(string, string)) (err error) { + + // Detect the freezer controller by checking k0s's cgroup for the existence + // of the cgroup.freeze file. + // https://github.com/torvalds/linux/blob/v5.3/Documentation/admin-guide/cgroup-v2.rst#processes + cgroupPath, err := cgroup2.NestedGroupPath("") + if err != nil { + return fmt.Errorf("failed to get k0s cgroup: %w", err) } + if cgroupPath != "/" { + cgroupPath = filepath.Join(g.mountPoint, cgroupPath) + } else { // The root cgroup cannot be frozen. Try to create a dummy cgroup. + tmpCgroupPath, err := os.MkdirTemp(g.mountPoint, "k0s-freezer-detection-*") + if err := os.Mkdir(tmpCgroupPath, 0700); err != nil { + if errors.Is(err, os.ErrPermission) && os.Geteuid() != 0 { + seen("freezer", "assumed, insufficient permissions, try with elevated permissions") + return nil + } + + return err + } + defer func() { err = errors.Join(err, os.Remove(tmpCgroupPath)) }() + cgroupPath = tmpCgroupPath + } + + // Check if the cgroup.freeze exists + freezePath := filepath.Join(cgroupPath, "cgroup.freeze") + stat, err := os.Stat(freezePath) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + if !stat.IsDir() { + seen("freezer", "cgroup.freeze exists") + } return nil }