[pve-devel] [PATCH container 1/2] add rebalance function from pvestatd to PVE::LXC
Wolfgang Bumiller
w.bumiller at proxmox.com
Thu Nov 17 11:27:02 CET 2016
pvestatd already depends on PVE::LXC and this way we can
use it at startup without having to import pvestatd.
Additionally: provide a way to get a new set of cores for a
not-yet started container.
---
src/PVE/LXC.pm | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 135 insertions(+)
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 3a284f7..598dff5 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -21,6 +21,7 @@ use PVE::Tools qw($IPV6RE $IPV4RE dir_glob_foreach lock_file lock_file_full O_PA
use PVE::Network;
use PVE::AccessControl;
use PVE::ProcFSTools;
+use PVE::CpuSet;
use PVE::LXC::Config;
use Time::HiRes qw (gettimeofday);
@@ -1499,5 +1500,139 @@ sub userns_command {
return [];
}
+# Container startup provides a $new_cores count. We then return a cpuset
+# the container will get assigned.
+sub rebalance_lxc_containers {
+ my ($new_cores) = @_;
+
+ return if !-d '/sys/fs/cgroup/cpuset/lxc'; # nothing to do...
+
+ my $all_cpus = PVE::CpuSet->new_from_cgroup('lxc', 'effective_cpus');
+ my @allowed_cpus = $all_cpus->members();
+ my $cpucount = scalar(@allowed_cpus);
+ my $max_cpuid = PVE::CpuSet::max_cpuid();
+
+ my @cpu_ctcount = (0) x $max_cpuid;
+ my @balanced_cts;
+
+ my $ctlist = PVE::LXC::config_list();
+
+ foreach my $vmid (sort keys %$ctlist) {
+ next if ! -d "/sys/fs/cgroup/cpuset/lxc/$vmid";
+
+ my ($conf, $cpuset);
+ eval {
+
+ $conf = PVE::LXC::Config->load_config($vmid);
+
+ $cpuset = PVE::CpuSet->new_from_cgroup("lxc/$vmid");
+ };
+ if (my $err = $@) {
+ warn $err;
+ next;
+ }
+
+ my @cpuset_members = $cpuset->members();
+
+ if (!PVE::LXC::Config->has_lxc_entry($conf, 'lxc.cgroup.cpuset.cpus')) {
+
+ my $cores = $conf->{cores} || $cpucount;
+ $cores = $cpucount if $cores > $cpucount;
+
+ # see if the number of cores was hot-reduced or
+ # hasn't been enacted at all yet
+ my $newset = PVE::CpuSet->new();
+ if ($cores < scalar(@cpuset_members)) {
+ $newset->insert(@cpuset_members[0..$cores-1]);
+ } elsif ($cores > scalar(@cpuset_members)) {
+ my $count = $newset->insert(@cpuset_members);
+ foreach my $cpu (@allowed_cpus) {
+ $count += $newset->insert($cpu);
+ last if $count >= $cores;
+ }
+ } else {
+ $newset->insert(@cpuset_members);
+ }
+
+ # Apply hot-plugged changes if any:
+ if (!$newset->is_equal($cpuset)) {
+ @cpuset_members = $newset->members();
+ syslog('info', "detected changed cpu set for lxc/$vmid: " .
+ $newset->short_string());
+ $newset->write_to_cgroup("lxc/$vmid");
+ }
+
+ # Note: no need to rebalance if we already use all cores
+ push @balanced_cts, [$vmid, $cores, $newset]
+ if defined($conf->{cores}) && ($cores != $cpucount);
+ }
+
+ foreach my $cpu (@cpuset_members) {
+ $cpu_ctcount[$cpu]++ if $cpu <= $max_cpuid;
+ }
+ }
+
+ my $find_best_cpu = sub {
+ my ($cpulist, $cpu) = @_;
+
+ my $cur_cost = $cpu_ctcount[$cpu];
+ my $cur_cpu = $cpu;
+
+ foreach my $candidate (@$cpulist) {
+ my $cost = $cpu_ctcount[$candidate];
+ if ($cost < ($cur_cost -1)) {
+ $cur_cost = $cost;
+ $cur_cpu = $candidate;
+ }
+ }
+
+ return $cur_cpu;
+ };
+
+ my $fill_cpus = sub {
+ my ($cores, $rest, @members) = @_;
+ my $newset = PVE::CpuSet->new();
+ foreach my $cpu (@members) {
+ my $best = &$find_best_cpu($rest, $cpu);
+ if ($best != $cpu) {
+ $cpu_ctcount[$best]++;
+ $cpu_ctcount[$cpu]--;
+ }
+ $newset->insert($best);
+ }
+ return $newset;
+ };
+
+ foreach my $bct (@balanced_cts) {
+ my ($vmid, $cores, $cpuset) = @$bct;
+
+ my $rest = [];
+ foreach my $cpu (@allowed_cpus) {
+ next if $cpuset->has($cpu);
+ push @$rest, $cpu;
+ }
+
+ my $newset = &$fill_cpus($cores, $rest, $cpuset->members());
+
+ if (!$newset->is_equal($cpuset)) {
+ syslog('info', "modified cpu set for lxc/$vmid: " .
+ $newset->short_string());
+ eval { $newset->write_to_cgroup("lxc/$vmid"); };
+ warn $@ if $@;
+ }
+ }
+
+ if ($new_cores) {
+ $new_cores = $cpucount if $cpucount < $new_cores;
+ # split allowed cpus into virtual lists of current vs rest cpus:
+ my @virtual_members = @allowed_cpus[0..$new_cores-1];
+ my $virtual_rest = [@allowed_cpus[$new_cores..$cpucount-1]];
+ # account for the cpus we're supposedly using:
+ $cpu_ctcount[$_]++ foreach @virtual_members;
+ return &$fill_cpus($new_cores, $virtual_rest, @virtual_members);
+ }
+
+ return undef;
+}
1;
--
2.1.4
More information about the pve-devel
mailing list