]>
Commit | Line | Data |
---|---|---|
80c7e72f WB |
1 | # cgroup handler |
2 | # | |
3 | # This package should deal with figuring out the right cgroup path for a | |
4 | # container (via the command socket), reading and writing cgroup values, and | |
5 | # handling cgroup v1 & v2 differences. | |
6 | # | |
7 | # Note that the long term plan is to have resource manage functions intead of | |
8 | # dealing with cgroup files on the outside. | |
9 | ||
10 | package PVE::LXC::CGroup; | |
11 | ||
12 | use strict; | |
13 | use warnings; | |
14 | ||
15 | use PVE::LXC::Command; | |
16 | ||
17 | # We don't want to do a command socket round trip for every cgroup read/write, | |
18 | # so any cgroup function needs to have the container's path cached, so this | |
19 | # package has to be instantiated. | |
20 | # | |
21 | # LXC keeps separate paths by controller (although they're normally all the | |
22 | # same, in our # case anyway), so we cache them by controller as well. | |
23 | sub new { | |
24 | my ($class, $vmid) = @_; | |
25 | ||
26 | my $self = { vmid => $vmid }; | |
27 | ||
28 | return bless $self, $class; | |
29 | } | |
30 | ||
31 | my $CPUSET_BASE = undef; | |
32 | # Find the cpuset cgroup controller. | |
33 | # | |
34 | # This is a function, not a method! | |
35 | sub cpuset_controller_path() { | |
36 | if (!defined($CPUSET_BASE)) { | |
37 | my $CPUSET_PATHS = [ | |
38 | # legacy cpuset cgroup: | |
39 | ['/sys/fs/cgroup/cpuset', 'cpuset.effective_cpus'], | |
40 | # pure cgroupv2 environment: | |
41 | ['/sys/fs/cgroup', 'cpuset.cpus.effective'], | |
42 | # hybrid, with cpuset moved to cgroupv2 | |
43 | ['/sys/fs/cgroup/unified', 'cpuset.cpus.effective'], | |
44 | ]; | |
45 | ||
46 | my ($result) = grep { -f "$_->[0]/$_->[1]" } @$CPUSET_PATHS; | |
47 | die "failed to find cpuset controller\n" if !defined($result); | |
48 | ||
49 | $CPUSET_BASE = $result->[0]; | |
50 | } | |
51 | ||
52 | return $CPUSET_BASE; | |
53 | } | |
54 | ||
55 | my $CGROUP_MODE = undef; | |
56 | # Figure out which cgroup mode we're operating under: | |
57 | # | |
58 | # Returns 1 if cgroupv1 controllers exist (hybrid or legacy mode), and 2 in a | |
59 | # cgroupv2-only environment. | |
60 | # | |
61 | # This is a function, not a method! | |
62 | sub cgroup_mode() { | |
63 | if (!defined($CGROUP_MODE)) { | |
64 | my ($v1, $v2) = PVE::LXC::get_cgroup_subsystems(); | |
65 | if (keys %$v1) { | |
66 | # hybrid or legacy mode | |
67 | $CGROUP_MODE = 1; | |
68 | } elsif ($v2) { | |
69 | $CGROUP_MODE = 2; | |
70 | } | |
71 | } | |
72 | ||
73 | die "unknown cgroup mode\n" if !defined($CGROUP_MODE); | |
74 | return $CGROUP_MODE; | |
75 | } | |
76 | ||
77 | # Get a subdirectory (without the cgroup mount point) for a controller. | |
78 | # | |
79 | # If `$controller` is `undef`, get the unified (cgroupv2) path. | |
80 | # | |
81 | # Note that in cgroup v2, lxc uses the activated controller names | |
82 | # (`cgroup.controllers` file) as list of controllers for the unified hierarchy, | |
83 | # so this returns a result when a `controller` is provided even when using | |
84 | # a pure cgroupv2 setup. | |
85 | my sub get_subdir { | |
86 | my ($self, $controller, $limiting) = @_; | |
87 | ||
88 | my $entry_name = $controller || 'unified'; | |
89 | my $entry = ($self->{controllers}->{$entry_name} //= {}); | |
90 | ||
91 | my $kind = $limiting ? 'limit' : 'ns'; | |
92 | my $path = $entry->{$kind}; | |
93 | ||
94 | return $path if defined $path; | |
95 | ||
96 | $path = PVE::LXC::Command::get_cgroup_path( | |
97 | $self->{vmid}, | |
98 | $controller, | |
99 | $limiting, | |
100 | ) or return undef; | |
101 | ||
102 | # untaint: | |
103 | if ($path =~ /\.\./) { | |
104 | die "lxc returned suspicious path: '$path'\n"; | |
105 | } | |
106 | ($path) = ($path =~ /^(.*)$/s); | |
107 | ||
108 | $entry->{$kind} = $path; | |
109 | ||
110 | return $path; | |
111 | } | |
112 | ||
113 | # Get a path for a controller. | |
114 | # | |
115 | # `$controller` may be `undef`, see get_subdir above for details. | |
116 | sub get_path { | |
117 | my ($self, $controller) = @_; | |
118 | ||
119 | my $path = get_subdir($self, $controller) | |
120 | or return undef; | |
121 | ||
122 | # The main mount point we currenlty assume to be in a standard location. | |
123 | return "/sys/fs/cgroup/$path" if cgroup_mode() == 2; | |
124 | return "/sys/fs/cgroup/unified/$path" if !defined($controller); | |
125 | return "/sys/fs/cgroup/$controller/$path"; | |
126 | } | |
127 | ||
128 | 1; |