package PVE::AutoBalloon; use warnings; use strict; sub compute_alg1 { my ($vmstatus, $goal, $maxchange, $debug) = @_; my $log = sub { print @_ if $debug; }; my $change_func = sub { my ($res, $idlist, $bytes) = @_; my $rest = $bytes; my $repeat = 1; my $done_hash = {}; my $progress = 1; while ($rest && $repeat && $progress) { $repeat = 0; $progress = 0; my $shares_total = 0; my $alloc_old = 0; foreach my $vmid (@$idlist) { next if defined($done_hash->{$vmid}); my $d = $vmstatus->{$vmid}; my $balloon = defined($res->{$vmid}) ? $res->{$vmid} : $d->{balloon}; $alloc_old += $balloon - $d->{balloon_min}; $shares_total += $d->{shares} || 1000; } my $changes = 0; my $alloc_new = $alloc_old + $rest; &$log("shares_total: $shares_total $alloc_new\n"); foreach my $vmid (@$idlist) { next if defined($done_hash->{$vmid}); my $d = $vmstatus->{$vmid}; my $shares = $d->{shares} || 1000; my $desired = $d->{balloon_min} + int(($alloc_new/$shares_total)*$shares); if ($desired > $d->{maxmem}) { $desired = $d->{maxmem}; $repeat = 1; } elsif ($desired < $d->{balloon_min}) { $desired = $d->{balloon_min}; $repeat = 1; } my ($new, $balloon); if (($bytes > 0) && ($desired - $d->{balloon}) > 0) { # grow $new = $d->{balloon} + $maxchange; $balloon = $new > $desired ? $desired : $new; } elsif (($desired - $d->{balloon}) < 0) { # shrink $new = $d->{balloon} - $maxchange; $balloon = $new > $desired ? $new : $desired; } else { $done_hash->{$vmid} = 1; next; } my $diff = $balloon - $d->{balloon}; if ($diff != 0) { my $oldballoon = defined($res->{$vmid}) ? $res->{$vmid} : $d->{balloon}; $res->{$vmid} = $balloon; my $change = $balloon - $oldballoon; if ($change != 0) { $changes += $change; my $absdiff = $diff > 0 ? $diff : -$diff; $progress += $absdiff; $repeat = 1; } &$log("change request for $vmid ($balloon, $diff, $desired, $new, $changes, $progress)\n"); } } $rest -= $changes; } return $rest; }; my $idlist = []; # list of VMs with working balloon river my $idlist1 = []; # list of VMs with memory pressure my $idlist2 = []; # list of VMs with enough free memory foreach my $vmid (keys %$vmstatus) { my $d = $vmstatus->{$vmid}; next if !$d->{balloon}; # skip if balloon driver not running next if !$d->{balloon_min}; # skip if balloon value not set in config next if $d->{lock} && $d->{lock} eq 'migrate'; next if defined($d->{shares}) && ($d->{shares} == 0); # skip if shares set to zero push @$idlist, $vmid; if ($d->{freemem} && ($d->{freemem} > $d->{balloon_min}*0.25) && ($d->{balloon} >= $d->{balloon_min})) { push @$idlist2, $vmid; &$log("idlist2 $vmid $d->{balloon}, $d->{balloon_min}, $d->{freemem}\n"); } else { push @$idlist1, $vmid; &$log("idlist1 $vmid $d->{balloon}, $d->{balloon_min}\n"); } } my $res = {}; if ($goal > 10*1024*1024) { &$log("grow request start $goal\n"); # priorize VMs with memory pressure my $rest = &$change_func($res, $idlist1, $goal); if ($rest >= $goal) { # no progress ==> consider all VMs &$log("grow request loop $rest\n"); $rest = &$change_func($res, $idlist, $rest); } &$log("grow request end $rest\n"); } elsif ($goal < -10*1024*1024) { &$log("shrink request $goal\n"); # priorize VMs with enough free memory my $rest = &$change_func($res, $idlist2, $goal); if ($rest <= $goal) { # no progress ==> consider all VMs &$log("shrink request loop $rest\n"); $rest = &$change_func($res, $idlist, $rest); } &$log("shrink request end $rest\n"); } else { &$log("do nothing\n"); # do nothing - requested change to small } foreach my $vmid (@$idlist) { next if !$res->{$vmid}; my $d = $vmstatus->{$vmid}; my $diff = int($res->{$vmid} - $d->{balloon}); my $absdiff = $diff < 0 ? -$diff : $diff; &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n"); } return $res; } 1;