]> git.proxmox.com Git - pve-common.git/blob - src/PVE/CpuSet.pm
bump version to 4.0-90
[pve-common.git] / src / PVE / CpuSet.pm
1 package PVE::CpuSet;
2
3 use strict;
4 use warnings;
5 use PVE::Tools;
6 use PVE::ProcFSTools;
7
8 my $MAX_CPUID;
9
10 sub 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 }
20
21 sub new {
22 my ($this) = @_;
23
24 my $class = ref($this) || $this;
25
26 my $self = bless { members => {} }, $class;
27
28 max_cpuid() if !defined($MAX_CPUID); # initialize $MAX_CPUID
29
30 return $self;
31 }
32
33 sub 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
69 sub 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");
85 close($fh) || die "failed to close '$filename' - $!\n";
86 }
87
88 sub 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
103 sub 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
118 sub has {
119 my ($self, $cpuid) = @_;
120
121 return $self->{members}->{$cpuid};
122 }
123
124 # members: this list is always sorted!
125 sub members {
126 my ($self) = @_;
127
128 return sort keys %{$self->{members}};
129 }
130
131 sub size {
132 my ($self) = @_;
133
134 return scalar(keys %{$self->{members}});
135 }
136
137 sub 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
153 sub 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);
167 if ($last != $next) {
168 $res .= "$last-$next";
169 } else {
170 $res .= "$last";
171 }
172 $last = $next = $cpu;
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
188 1;