CpuSet: Simply class to handle cpu sets
[pve-common.git] / src / PVE / CpuSet.pm
1 package PVE::CpuSet;
2
3 use strict;
4 use warnings;
5 use PVE::Tools;
6
7 our $MAX_CPUID = 256; # should be enough for the next years
8
9 sub new {
10     my ($this) = @_;
11
12     my $class = ref($this) || $this;
13
14     my $self = bless { members => {} }, $class;
15
16     return $self;
17 }
18
19 sub new_from_cgroup {
20     my ($this, $cgroup, $kind) = @_;
21
22     $kind //= 'cpus';
23
24     my $filename = "/sys/fs/cgroup/cpuset/$cgroup/cpuset.$kind";
25     my $set_text = PVE::Tools::file_read_firstline($filename) // '';
26
27     my $cpuset = $this->new();
28     
29     my $members = $cpuset->{members};
30
31     my $count = 0;
32
33     foreach my $part (split(/,/, $set_text)) {
34         if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
35             my ($from, $to) = ($1, $2);
36             $to //= $1;
37             die "cpu id '$from' is out of range\n" if $from >= $MAX_CPUID;
38             die "cpu id '$to' is out of range\n" if $to >= $MAX_CPUID;
39             die "invalid range: $part ($to < $from)\n" if $to < $from;
40             for (my $i = $from; $i <= $to; $i++) {
41                 $members->{$i} = 1;
42                 $count++;
43             };
44         } else {
45             die "invalid range: $part\n";
46         }
47     }
48
49     die "got empty cpuset for cgroup '$cgroup'\n"
50         if !$count;
51
52     return $cpuset;
53 }
54
55 sub write_to_cgroup {
56     my ($self, $cgroup) = @_;
57
58     my $filename = "/sys/fs/cgroup/cpuset/$cgroup/cpuset.cpus";
59
60     my $value = '';
61     my @members = $self->members();
62     foreach my $cpuid (@members) {
63         $value .= ',' if length($value);
64         $value .= $cpuid;
65     }
66
67     die "unable to write empty cpu set\n" if !length($value);
68
69     open(my $fh, '>', $filename) || die "failed to open '$filename' - $!\n";
70     PVE::Tools::safe_print($filename, $fh, "$value\n");
71     close($fh);
72 }
73
74 sub insert {
75     my ($self, @members) = @_;
76
77     my $count = 0;
78     
79     foreach my $cpu (@members) {
80         die "cpu id '$cpu' is out of range\n" if $cpu >= $MAX_CPUID;
81         next if $self->{members}->{$cpu};
82         $self->{members}->{$cpu} = 1;
83         $count++;
84     }
85
86     return $count;
87 }
88
89 sub delete {
90     my ($self, @members) = @_;
91
92     my $count = 0;
93     
94     foreach my $cpu (@members) {
95         die "cpu id '$cpu' is out of range\n" if $cpu >= $MAX_CPUID;
96         next if !$self->{members}->{$cpu};
97         delete $self->{members}->{$cpu};
98         $count++;
99     }
100
101     return $count;
102 }
103
104 sub has {
105    my ($self, $cpuid) = @_;
106
107    return $self->{members}->{$cpuid};
108 }
109
110 # members: this list is always sorted!
111 sub members {
112     my ($self) = @_;
113
114     return sort keys %{$self->{members}};
115 }    
116
117 sub size {
118     my ($self) = @_;
119
120     return scalar(keys %{$self->{members}});
121 }
122
123 sub is_equal {
124     my ($self, $set2) = @_;
125
126     my $members1 = $self->{members};
127     my $members2 = $set2->{members};
128
129     foreach my $id (keys %$members1) {
130         return 0 if !$members2->{$id};
131     }
132     foreach my $id (keys %$members2) {
133         return 0 if !$members1->{$id};
134     }
135     
136     return 1;
137 }
138
139 1;