]> git.proxmox.com Git - pve-manager.git/blob - PVE/AutoBalloon.pm
8f4d48a4548ec6bf2bb70c8f2b1a3e234e895558
[pve-manager.git] / PVE / AutoBalloon.pm
1 package PVE::AutoBalloon;
2
3 use warnings;
4 use strict;
5
6 sub compute_alg1 {
7 my ($vmstatus, $goal, $maxchange, $debug) = @_;
8
9 my $log = sub { print @_ if $debug; };
10
11 my $change_func = sub {
12 my ($res, $idlist, $bytes) = @_;
13
14 my $rest = $bytes;
15 my $repeat = 1;
16 my $done_hash = {};
17 my $progress = 1;
18
19 while ($rest && $repeat && $progress) {
20 $repeat = 0;
21 $progress = 0;
22
23 my $shares_total = 0;
24 my $alloc_old = 0;
25
26 foreach my $vmid (@$idlist) {
27 next if defined($done_hash->{$vmid});
28 my $d = $vmstatus->{$vmid};
29 my $balloon = defined($res->{$vmid}) ? $res->{$vmid} : $d->{balloon};
30 $alloc_old += $balloon - $d->{balloon_min};
31 $shares_total += $d->{shares} || 1000;
32 }
33
34 my $changes = 0;
35
36 my $alloc_new = $alloc_old + $rest;
37
38 &$log("shares_total: $shares_total $alloc_new\n");
39
40 foreach my $vmid (@$idlist) {
41 next if defined($done_hash->{$vmid});
42 my $d = $vmstatus->{$vmid};
43 my $shares = $d->{shares} || 1000;
44 my $desired = $d->{balloon_min} + int(($alloc_new/$shares_total)*$shares);
45
46 if ($desired > $d->{maxmem}) {
47 $desired = $d->{maxmem};
48 $repeat = 1;
49 } elsif ($desired < $d->{balloon_min}) {
50 $desired = $d->{balloon_min};
51 $repeat = 1;
52 }
53
54 my ($new, $balloon);
55 if (($bytes > 0) && ($desired - $d->{balloon}) > 0) { # grow
56 $new = $d->{balloon} + $maxchange;
57 $balloon = $new > $desired ? $desired : $new;
58 } elsif (($desired - $d->{balloon}) < 0) { # shrink
59 $new = $d->{balloon} - $maxchange;
60 $balloon = $new > $desired ? $new : $desired;
61 } else {
62 $done_hash->{$vmid} = 1;
63 next;
64 }
65
66 my $diff = $balloon - $d->{balloon};
67 if ($diff != 0) {
68 my $oldballoon = defined($res->{$vmid}) ? $res->{$vmid} : $d->{balloon};
69 $res->{$vmid} = $balloon;
70 my $change = $balloon - $oldballoon;
71 if ($change != 0) {
72 $changes += $change;
73 my $absdiff = $diff > 0 ? $diff : -$diff;
74 $progress += $absdiff;
75 $repeat = 1;
76 }
77 &$log("change request for $vmid ($balloon, $diff, $desired, $new, $changes, $progress)\n");
78 }
79 }
80
81 $rest -= $changes;
82 }
83
84 return $rest;
85 };
86
87
88 my $idlist = []; # list of VMs with working balloon river
89 my $idlist1 = []; # list of VMs with memory pressure
90 my $idlist2 = []; # list of VMs with enough free memory
91
92 foreach my $vmid (keys %$vmstatus) {
93 my $d = $vmstatus->{$vmid};
94 next if !$d->{balloon}; # skip if balloon driver not running
95 next if !$d->{balloon_min}; # skip if balloon value not set in config
96 next if $d->{lock} && $d->{lock} eq 'migrate';
97 next if defined($d->{shares}) &&
98 ($d->{shares} == 0); # skip if shares set to zero
99
100 push @$idlist, $vmid;
101
102 if ($d->{freemem} &&
103 ($d->{freemem} > $d->{balloon_min}*0.25) &&
104 ($d->{balloon} >= $d->{balloon_min})) {
105 push @$idlist2, $vmid;
106 &$log("idlist2 $vmid $d->{balloon}, $d->{balloon_min}, $d->{freemem}\n");
107 } else {
108 push @$idlist1, $vmid;
109 &$log("idlist1 $vmid $d->{balloon}, $d->{balloon_min}\n");
110 }
111 }
112
113 my $res = {};
114
115 if ($goal > 10*1024*1024) {
116 &$log("grow request start $goal\n");
117 # priorize VMs with memory pressure
118 my $rest = &$change_func($res, $idlist1, $goal);
119 if ($rest >= $goal) { # no progress ==> consider all VMs
120 &$log("grow request loop $rest\n");
121 $rest = &$change_func($res, $idlist, $rest);
122 }
123 &$log("grow request end $rest\n");
124
125 } elsif ($goal < -10*1024*1024) {
126 &$log("shrink request $goal\n");
127 # priorize VMs with enough free memory
128 my $rest = &$change_func($res, $idlist2, $goal);
129 if ($rest <= $goal) { # no progress ==> consider all VMs
130 &$log("shrink request loop $rest\n");
131 $rest = &$change_func($res, $idlist, $rest);
132 }
133 &$log("shrink request end $rest\n");
134 } else {
135 &$log("do nothing\n");
136 # do nothing - requested change to small
137 }
138
139 foreach my $vmid (@$idlist) {
140 next if !$res->{$vmid};
141 my $d = $vmstatus->{$vmid};
142 my $diff = int($res->{$vmid} - $d->{balloon});
143 my $absdiff = $diff < 0 ? -$diff : $diff;
144 &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n");
145 }
146 return $res;
147 }
148
149 1;