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