9f76f385d27843282cc617167b751020d88539bd
[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;