]> git.proxmox.com Git - pve-common.git/blame - src/PVE/CpuSet.pm
cpuset: cgroupv2 support
[pve-common.git] / src / PVE / CpuSet.pm
CommitLineData
a1c3f18e
DM
1package PVE::CpuSet;
2
3use strict;
4use warnings;
5use PVE::Tools;
ae97d553 6use PVE::ProcFSTools;
a1c3f18e 7
a1c3f18e 8sub new {
591be969 9 my ($class, $members) = @_;
a1c3f18e 10
591be969
WB
11 $members //= {};
12 my $self = bless { members => $members }, $class;
3f76daba 13
a1c3f18e
DM
14 return $self;
15}
16
dd13f183 17# Create a new set with the contents of a cgroup-v1 subdirectory.
a1c3f18e 18sub new_from_cgroup {
3e02302a 19 my ($class, $cgroup, $effective) = @_;
a1c3f18e 20
dd13f183
WB
21 return $class->new_from_path("/sys/fs/cgroup/cpuset/$cgroup", $effective);
22}
23
24# Create a new set from the contents of a complete path to a cgroup directory.
25sub new_from_path {
26 my ($class, $path, $effective) = @_;
27
7c3db4b8
WB
28 my $filename;
29 if ($effective) {
30 $filename = "$path/cpuset.effective_cpus";
31 if (!-e $filename) {
32 # cgroupv2:
33 $filename = "$path/cpuset.cpus.effective";
34 }
35 } else {
36 $filename = "$path/cpuset.cpus";
37 }
a1c3f18e 38
a1c3f18e
DM
39 my $set_text = PVE::Tools::file_read_firstline($filename) // '';
40
591be969 41 my ($count, $members) = parse_cpuset($set_text);
9bbc4e17 42
dd13f183 43 die "got empty cpuset for cgroup '$path'\n"
591be969
WB
44 if !$count;
45
46 return $class->new($members);
47}
a1c3f18e 48
591be969
WB
49sub parse_cpuset {
50 my ($set_text) = @_;
51
52 my $members = {};
a1c3f18e
DM
53 my $count = 0;
54
55 foreach my $part (split(/,/, $set_text)) {
56 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
57 my ($from, $to) = ($1, $2);
58 $to //= $1;
a1c3f18e
DM
59 die "invalid range: $part ($to < $from)\n" if $to < $from;
60 for (my $i = $from; $i <= $to; $i++) {
61 $members->{$i} = 1;
62 $count++;
63 };
64 } else {
65 die "invalid range: $part\n";
66 }
67 }
68
591be969 69 return ($count, $members);
a1c3f18e
DM
70}
71
72sub write_to_cgroup {
73 my ($self, $cgroup) = @_;
74
75 my $filename = "/sys/fs/cgroup/cpuset/$cgroup/cpuset.cpus";
76
77 my $value = '';
78 my @members = $self->members();
79 foreach my $cpuid (@members) {
80 $value .= ',' if length($value);
81 $value .= $cpuid;
82 }
83
84 die "unable to write empty cpu set\n" if !length($value);
85
86 open(my $fh, '>', $filename) || die "failed to open '$filename' - $!\n";
87 PVE::Tools::safe_print($filename, $fh, "$value\n");
dbfaa2b8 88 close($fh) || die "failed to close '$filename' - $!\n";
a1c3f18e
DM
89}
90
91sub insert {
92 my ($self, @members) = @_;
93
94 my $count = 0;
9bbc4e17 95
a1c3f18e 96 foreach my $cpu (@members) {
a1c3f18e
DM
97 next if $self->{members}->{$cpu};
98 $self->{members}->{$cpu} = 1;
99 $count++;
100 }
101
102 return $count;
103}
104
105sub delete {
106 my ($self, @members) = @_;
107
108 my $count = 0;
9bbc4e17 109
a1c3f18e 110 foreach my $cpu (@members) {
a1c3f18e
DM
111 next if !$self->{members}->{$cpu};
112 delete $self->{members}->{$cpu};
113 $count++;
114 }
115
116 return $count;
117}
118
119sub has {
120 my ($self, $cpuid) = @_;
121
122 return $self->{members}->{$cpuid};
123}
124
125# members: this list is always sorted!
126sub members {
127 my ($self) = @_;
128
1cffb285 129 return sort { $a <=> $b } keys %{$self->{members}};
9bbc4e17 130}
a1c3f18e
DM
131
132sub size {
133 my ($self) = @_;
134
135 return scalar(keys %{$self->{members}});
136}
137
138sub is_equal {
139 my ($self, $set2) = @_;
140
141 my $members1 = $self->{members};
142 my $members2 = $set2->{members};
143
144 foreach my $id (keys %$members1) {
145 return 0 if !$members2->{$id};
146 }
147 foreach my $id (keys %$members2) {
148 return 0 if !$members1->{$id};
149 }
9bbc4e17 150
a1c3f18e
DM
151 return 1;
152}
153
284dca70
DM
154sub short_string {
155 my ($self) = @_;
156
157 my @members = $self->members();
158
159 my $res = '';
160 my ($last, $next);
161 foreach my $cpu (@members) {
162 if (!defined($last)) {
163 $last = $next = $cpu;
164 } elsif (($next + 1) == $cpu) {
165 $next = $cpu;
166 } else {
167 $res .= ',' if length($res);
74116083
DM
168 if ($last != $next) {
169 $res .= "$last-$next";
170 } else {
171 $res .= "$last";
172 }
9c07db40 173 $last = $next = $cpu;
284dca70
DM
174 }
175 }
176
177 if (defined($last)) {
178 $res .= ',' if length($res);
179 if ($last != $next) {
180 $res .= "$last-$next";
181 } else {
182 $res .= "$last";
183 }
184 }
185
186 return $res;
187}
188
a1c3f18e 1891;