]> git.proxmox.com Git - pve-container.git/commitdiff
cgroup: more generic get_cgroup_controllers function
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 9 Apr 2020 11:28:37 +0000 (13:28 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 29 Apr 2020 06:54:47 +0000 (08:54 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
src/PVE/LXC.pm
src/PVE/LXC/CGroup.pm

index 9382700983cd6ec654b4e39990269227b3e5cc93..3b1c0d6702169a984b5a4ef854409f9ce42c8bc1 100644 (file)
@@ -401,19 +401,9 @@ sub parse_ipv4_cidr {
     die "unable to parse ipv4 address/mask\n";
 }
 
+# Deprecated. Use `PVE::LXC::CGroup::get_cgroup_controllers()` instead.
 sub get_cgroup_subsystems {
-       my $v1 = {};
-       my $v2 = 0;
-       my $data = PVE::Tools::file_get_contents('/proc/self/cgroup');
-       while ($data =~ /^\d+:([^:\n]*):.*$/gm) {
-               my $type = $1;
-               if (length($type)) {
-                       $v1->{$_} = 1 foreach split(/,/, $type);
-               } else {
-                       $v2 = 1;
-               }
-       }
-       return wantarray ? ($v1, $v2) : $v1;
+    PVE::LXC::CGroup::get_v1_controllers();
 }
 
 # With seccomp trap to userspace we now have the ability to optionally forward
index 8ad1627e47340b18d3fce3d6d3d9d0fcb2d3d2a6..7f232033821e71613b98b35d4e4647bee7a73b0e 100644 (file)
@@ -36,28 +36,53 @@ sub new {
     return bless $self, $class;
 }
 
-my $CPUSET_BASE = undef;
-# Find the cpuset cgroup controller.
+# Get the v1 controller list.
 #
-# This is a function, not a method!
-sub cpuset_controller_path() {
-    if (!defined($CPUSET_BASE)) {
-       my $CPUSET_PATHS = [
-           # legacy cpuset cgroup:
-           ['/sys/fs/cgroup/cpuset',  'cpuset.effective_cpus'],
-           # pure cgroupv2 environment:
-           ['/sys/fs/cgroup',         'cpuset.cpus.effective'],
-           # hybrid, with cpuset moved to cgroupv2
-           ['/sys/fs/cgroup/unified', 'cpuset.cpus.effective'],
-       ];
-
-       my ($result) = grep { -f "$_->[0]/$_->[1]" } @$CPUSET_PATHS;
-       die "failed to find cpuset controller\n" if !defined($result);
-
-       $CPUSET_BASE = $result->[0];
+# Returns a set (hash mapping names to `1`) of cgroupv1 controllers, and an
+# optional boolean whether a unified (cgroupv2) hierarchy exists.
+#
+# Deprecated: Use `get_cgroup_controllers()` instead.
+sub get_v1_controllers {
+    my $v1 = {};
+    my $v2 = 0;
+    my $data = PVE::Tools::file_get_contents('/proc/self/cgroup');
+    while ($data =~ /^\d+:([^:\n]*):.*$/gm) {
+       my $type = $1;
+       if (length($type)) {
+           $v1->{$_} = 1 foreach split(/,/, $type);
+       } else {
+           $v2 = 1;
+       }
     }
+    return wantarray ? ($v1, $v2) : $v1;
+}
+
+# Get the set v2 controller list from the `cgroup.controllers` file.
+my sub get_v2_controllers {
+    my $v2 = eval { file_get_contents('/sys/fs/cgroup/cgroup.controllers') }
+       || eval { file_get_contents('/sys/fs/cgroup/unified/cgroup.controllers') };
+    return undef if !defined $v2;
 
-    return $CPUSET_BASE;
+    # It's a simple space separated list:
+    return { map { $_ => 1 } split(/\s+/, $v2) };
+}
+
+my $CGROUP_CONTROLLERS = undef;
+# Get a list of controllers enabled in each cgroup subsystem.
+#
+# This is a more complete version of `PVE::LXC::get_cgroup_subsystems`.
+#
+# Returns 2 sets (hashes mapping controller names to `1`), one for each cgroup
+# version.
+sub get_cgroup_controllers() {
+    if (!defined($CGROUP_CONTROLLERS)) {
+       my ($v1, undef) = get_v1_controllers();
+       my $v2 = get_v2_controllers();
+
+       $CGROUP_CONTROLLERS = [$v1, $v2];
+    }
+
+    return $CGROUP_CONTROLLERS->@*;
 }
 
 my $CGROUP_MODE = undef;
@@ -66,10 +91,13 @@ my $CGROUP_MODE = undef;
 # Returns 1 if cgroupv1 controllers exist (hybrid or legacy mode), and 2 in a
 # cgroupv2-only environment.
 #
+# NOTE: To fully support a hybrid layout it is better to use functions like
+# `cpuset_controller_path`.
+#
 # This is a function, not a method!
 sub cgroup_mode() {
     if (!defined($CGROUP_MODE)) {
-       my ($v1, $v2) = PVE::LXC::get_cgroup_subsystems();
+       my ($v1, $v2) = get_cgroup_controllers();
        if (keys %$v1) {
            # hybrid or legacy mode
            $CGROUP_MODE = 1;
@@ -82,6 +110,49 @@ sub cgroup_mode() {
     return $CGROUP_MODE;
 }
 
+# Find a cgroup controller and return its path and version.
+#
+# LXC initializes the unified hierarchy first, so if a controller is
+# available via both we favor cgroupv2 here as well.
+#
+# Returns nothing if the controller is not available.
+sub find_cgroup_controller($) {
+    my ($controller) = @_;
+
+    my ($v1, $v2) = get_cgroup_controllers();
+
+    if (!defined($controller) || $v2->{$controller}) {
+       my $path;
+       if (cgroup_mode() == 2) {
+           $path = '/sys/fs/cgroup';
+       } else {
+           $path = '/sys/fs/cgroup/unified';
+       }
+       return wantarray ? ($path, 2) : $path;
+    }
+
+    if (defined($controller) && $v1->{$controller}) {
+       my $path = "/sys/fs/cgroup/$controller";
+       return wantarray ? ($path, 1) : $path;
+    }
+
+    return;
+}
+
+my $CG_PATH_CPUSET = undef;
+my $CG_VER_CPUSET = undef;
+# Find the cpuset cgroup controller.
+#
+# This is a function, not a method!
+sub cpuset_controller_path() {
+    if (!defined($CG_PATH_CPUSET)) {
+       ($CG_PATH_CPUSET, $CG_VER_CPUSET) = find_cgroup_controller('cpuset')
+           or die "failed to find cpuset controller\n";
+    }
+
+    return wantarray ? ($CG_PATH_CPUSET, $CG_VER_CPUSET) : $CG_PATH_CPUSET;
+}
+
 # Get a subdirectory (without the cgroup mount point) for a controller.
 #
 # If `$controller` is `undef`, get the unified (cgroupv2) path.
@@ -118,19 +189,37 @@ my sub get_subdir {
     return $path;
 }
 
-# Get a path for a controller.
+# Get path and version for a controller.
 #
 # `$controller` may be `undef`, see get_subdir above for details.
+#
+# Returns either just the path, or the path and cgroup version as a tuple.
 sub get_path {
     my ($self, $controller) = @_;
 
+    # Find the controller before querying the lxc monitor via a socket:
+    my ($cgpath, $ver) = find_cgroup_controller($controller)
+       or return undef;
+
     my $path = get_subdir($self, $controller)
        or return undef;
 
-    # The main mount point we currently assume to be in a standard location.
-    return "/sys/fs/cgroup/$path" if cgroup_mode() == 2;
-    return "/sys/fs/cgroup/unified/$path" if !defined($controller);
-    return "/sys/fs/cgroup/$controller/$path";
+    $path = "$cgpath/$path";
+    return wantarray ? ($path, $ver) : $path;
+}
+
+# Convenience method to get the path info if the first existing controller.
+#
+# Returns the same as `get_path`.
+sub get_any_path {
+    my ($self, @controllers) = @_;
+
+    my ($path, $ver);
+    for my $c (@controllers) {
+       ($path, $ver) = $self->get_path($c);
+       last if defined $path;
+    }
+    return wantarray ? ($path, $ver) : $path;
 }
 
 # Parse a 'Nested keyed' file: