~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/xend/balloon.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#===========================================================================
 
2
# This library is free software; you can redistribute it and/or
 
3
# modify it under the terms of version 2.1 of the GNU Lesser General Public
 
4
# License as published by the Free Software Foundation.
 
5
#
 
6
# This library is distributed in the hope that it will be useful,
 
7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
8
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
9
# Lesser General Public License for more details.
 
10
#
 
11
# You should have received a copy of the GNU Lesser General Public
 
12
# License along with this library; if not, write to the Free Software
 
13
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
14
#============================================================================
 
15
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
 
16
# Copyright (C) 2005 XenSource Ltd
 
17
#============================================================================
 
18
 
 
19
 
 
20
import time
 
21
 
 
22
import xen.lowlevel.xc
 
23
 
 
24
import XendDomain
 
25
import XendOptions
 
26
import MemoryPool
 
27
from XendLogging import log
 
28
from XendError import VmError
 
29
import osdep
 
30
from xen.xend.XendConstants import *
 
31
 
 
32
RETRY_LIMIT = 20
 
33
RETRY_LIMIT_INCR = 5
 
34
##
 
35
# The time to sleep between retries grows linearly, using this value (in
 
36
# seconds).  When the system is lightly loaded, memory should be scrubbed and
 
37
# returned to the system very quickly, whereas when it is loaded, the system
 
38
# needs idle time to get the scrubbing done.  This linear growth accommodates
 
39
# such requirements.
 
40
SLEEP_TIME_GROWTH = 0.1
 
41
 
 
42
# A mapping between easy-to-remember labels and the more verbose
 
43
# label actually shown in the PROC_XEN_BALLOON file.
 
44
#labels = { 'current'      : 'Current allocation',
 
45
#           'target'       : 'Requested target',
 
46
#           'low-balloon'  : 'Low-mem balloon',
 
47
#           'high-balloon' : 'High-mem balloon',
 
48
#           'limit'        : 'Xen hard limit' }
 
49
 
 
50
def _get_proc_balloon(label):
 
51
    """Returns the value for the named label.  Returns None if the label was
 
52
       not found or the value was non-numeric."""
 
53
 
 
54
    return osdep.lookup_balloon_stat(label)
 
55
 
 
56
def get_dom0_current_alloc():
 
57
    """Returns the current memory allocation (in KiB) of dom0."""
 
58
 
 
59
    kb = _get_proc_balloon('current')
 
60
    if kb == None:
 
61
        raise VmError('Failed to query current memory allocation of dom0.')
 
62
    return kb
 
63
 
 
64
def get_dom0_target_alloc():
 
65
    """Returns the target memory allocation (in KiB) of dom0."""
 
66
 
 
67
    kb = _get_proc_balloon('target')
 
68
    if kb == None:
 
69
        raise VmError('Failed to query target memory allocation of dom0.')
 
70
    return kb
 
71
 
 
72
def free(need_mem, dominfo):
 
73
    """Balloon out memory from the privileged domain so that there is the
 
74
    specified required amount (in KiB) free.
 
75
    """
 
76
 
 
77
    # We check whether there is enough free memory, and if not, instruct dom0
 
78
    # to balloon out to free some up.  Memory freed by a destroyed domain may
 
79
    # not appear in the free_memory field immediately, because it needs to be
 
80
    # scrubbed before it can be released to the free list, which is done
 
81
    # asynchronously by Xen; ballooning is asynchronous also.  Such memory
 
82
    # does, however, need to be accounted for when calculating how much dom0
 
83
    # needs to balloon.  No matter where we expect the free memory to come
 
84
    # from, we need to wait for it to become available.
 
85
    #
 
86
    # We are not allowed to balloon below dom0_min_mem, or if dom0_ballooning
 
87
    # is False, we cannot balloon at all.  Memory can still become available
 
88
    # through a rebooting domain, however.
 
89
    #
 
90
    # Eventually, we time out (presumably because there really isn't enough
 
91
    # free memory).
 
92
    #
 
93
    # We don't want to set the memory target (triggering a watch) when that
 
94
    # has already been done, but we do want to respond to changing memory
 
95
    # usage, so we recheck the required alloc each time around the loop, but
 
96
    # track the last used value so that we don't trigger too many watches.
 
97
 
 
98
    xoptions = XendOptions.instance()
 
99
    dom0 = XendDomain.instance().privilegedDomain()
 
100
    xc = xen.lowlevel.xc.xc()
 
101
    memory_pool = MemoryPool.instance() 
 
102
    try:
 
103
        dom0_min_mem = xoptions.get_dom0_min_mem() * 1024
 
104
        dom0_ballooning = xoptions.get_enable_dom0_ballooning()
 
105
        guest_size = 0
 
106
        hvm = dominfo.info.is_hvm()
 
107
        if memory_pool.is_enabled() and dominfo.domid:
 
108
            if not hvm :
 
109
                if need_mem <= 4 * 1024: 
 
110
                    guest_size = 32
 
111
                else:
 
112
                    guest_size = dominfo.image.getBitSize()
 
113
            if guest_size == 32:
 
114
                dom0_ballooning = 0
 
115
        else: #no ballooning as memory pool enabled
 
116
            dom0_ballooning = xoptions.get_enable_dom0_ballooning()
 
117
        dom0_alloc = get_dom0_current_alloc()
 
118
 
 
119
        retries = 0
 
120
        sleep_time = SLEEP_TIME_GROWTH
 
121
        new_alloc = 0
 
122
        last_new_alloc = None
 
123
        last_free = None
 
124
        rlimit = RETRY_LIMIT
 
125
        mem_need_balloon = 0
 
126
        left_memory_pool = 0
 
127
        mem_target = 0
 
128
        untouched_memory_pool = 0
 
129
        real_need_mem = need_mem
 
130
 
 
131
        # stop tmem from absorbing any more memory (must THAW when done!)
 
132
        xc.tmem_control(0,TMEMC_FREEZE,-1, 0, 0, 0, "")
 
133
 
 
134
        # If unreasonable memory size is required, we give up waiting
 
135
        # for ballooning or scrubbing, as if had retried.
 
136
        physinfo = xc.physinfo()
 
137
        free_mem = physinfo['free_memory']
 
138
        scrub_mem = physinfo['scrub_memory']
 
139
        total_mem = physinfo['total_memory']
 
140
        if memory_pool.is_enabled() and dominfo.domid:
 
141
            if guest_size != 32 or hvm:
 
142
                if need_mem > 4 * 1024: 
 
143
                    dominfo.alloc_mem = need_mem
 
144
                left_memory_pool = memory_pool.get_left_memory()
 
145
                if need_mem > left_memory_pool:
 
146
                    dominfo.alloc_mem = 0
 
147
                    raise VmError(('Not enough free memory'
 
148
                                   ' so I cannot release any more.  '
 
149
                                   'I need %d KiB but only have %d in the pool.') %
 
150
                                   (need_mem, memory_pool.get_left_memory()))
 
151
                else:
 
152
                    untouched_memory_pool = memory_pool.get_untouched_memory()
 
153
                    if (left_memory_pool - untouched_memory_pool) > need_mem:
 
154
                        dom0_ballooning = 0
 
155
                    else:
 
156
                        mem_need_balloon = need_mem - left_memory_pool + untouched_memory_pool
 
157
                        need_mem = free_mem + scrub_mem + mem_need_balloon
 
158
 
 
159
        if dom0_ballooning:
 
160
            max_free_mem = total_mem - dom0_min_mem
 
161
        else:
 
162
            max_free_mem = total_mem - dom0_alloc
 
163
        if need_mem >= max_free_mem:
 
164
            retries = rlimit
 
165
 
 
166
        freeable_mem = free_mem + scrub_mem
 
167
        if freeable_mem < need_mem and need_mem < max_free_mem:
 
168
            # flush memory from tmem to scrub_mem and reobtain physinfo
 
169
            need_tmem_kb = need_mem - freeable_mem
 
170
            tmem_kb = xc.tmem_control(0,TMEMC_FLUSH,-1, need_tmem_kb, 0, 0, "")
 
171
            log.debug("Balloon: tmem relinquished %d KiB of %d KiB requested.",
 
172
                      tmem_kb, need_tmem_kb)
 
173
            physinfo = xc.physinfo()
 
174
            free_mem = physinfo['free_memory']
 
175
            scrub_mem = physinfo['scrub_memory']
 
176
 
 
177
        # Check whethercurrent machine is a numa system and the new 
 
178
        # created hvm has all its vcpus in the same node, if all the 
 
179
        # conditions above are fit. We will wait until all the pages 
 
180
        # in scrub list are freed (if waiting time go beyond 20s, 
 
181
        # we will stop waiting it.)
 
182
        if physinfo['nr_nodes'] > 1 and retries == 0:
 
183
            oldnode = -1
 
184
            waitscrub = 1
 
185
            vcpus = dominfo.info['cpus'][0]
 
186
            for vcpu in vcpus:
 
187
                nodenum = 0
 
188
                for node in physinfo['node_to_cpu']:
 
189
                    for cpu in node:
 
190
                        if vcpu == cpu:
 
191
                            if oldnode == -1:
 
192
                                oldnode = nodenum
 
193
                            elif oldnode != nodenum:
 
194
                                waitscrub = 0
 
195
                    nodenum = nodenum + 1
 
196
 
 
197
            if waitscrub == 1 and scrub_mem > 0:
 
198
                log.debug("wait for scrub %s", scrub_mem)
 
199
                while scrub_mem > 0 and retries < rlimit:
 
200
                    time.sleep(sleep_time)
 
201
                    physinfo = xc.physinfo()
 
202
                    free_mem = physinfo['free_memory']
 
203
                    scrub_mem = physinfo['scrub_memory']
 
204
                    retries += 1
 
205
                    sleep_time += SLEEP_TIME_GROWTH
 
206
                log.debug("scrub for %d times", retries)
 
207
 
 
208
            retries = 0
 
209
            sleep_time = SLEEP_TIME_GROWTH
 
210
        while retries < rlimit:
 
211
            physinfo = xc.physinfo()
 
212
            free_mem = physinfo['free_memory']
 
213
            scrub_mem = physinfo['scrub_memory']
 
214
            if free_mem >= need_mem:
 
215
                if (guest_size != 32 or hvm) and dominfo.domid:
 
216
                    memory_pool.decrease_untouched_memory(mem_need_balloon)
 
217
                    memory_pool.decrease_memory(real_need_mem)
 
218
                else:
 
219
                    log.debug("Balloon: %d KiB free; need %d; done.",
 
220
                              free_mem, need_mem)
 
221
                return
 
222
 
 
223
            if retries == 0:
 
224
                rlimit += ((need_mem - free_mem)/1024/1024) * RETRY_LIMIT_INCR
 
225
                log.debug("Balloon: %d KiB free; %d to scrub; need %d; retries: %d.",
 
226
                          free_mem, scrub_mem, need_mem, rlimit)
 
227
 
 
228
            if dom0_ballooning:
 
229
                dom0_alloc = get_dom0_current_alloc()
 
230
                new_alloc = dom0_alloc - (need_mem - free_mem - scrub_mem)
 
231
                if free_mem + scrub_mem >= need_mem:
 
232
                    if last_new_alloc == None:
 
233
                        log.debug("Balloon: waiting on scrubbing")
 
234
                        last_new_alloc = dom0_alloc
 
235
                else:
 
236
                    if (new_alloc >= dom0_min_mem and
 
237
                        new_alloc != last_new_alloc):
 
238
                        new_alloc_mb = new_alloc / 1024  # Round down
 
239
                        log.debug("Balloon: setting dom0 target to %d MiB.",
 
240
                                  new_alloc_mb)
 
241
                        dom0.setMemoryTarget(new_alloc_mb)
 
242
                        last_new_alloc = new_alloc
 
243
                # Continue to retry, waiting for ballooning or scrubbing.
 
244
 
 
245
            time.sleep(sleep_time)
 
246
            if retries < 2 * RETRY_LIMIT:
 
247
                sleep_time += SLEEP_TIME_GROWTH
 
248
            if last_free != None and last_free >= free_mem + scrub_mem:
 
249
                retries += 1
 
250
            last_free = free_mem + scrub_mem
 
251
 
 
252
        # Not enough memory; diagnose the problem.
 
253
        if not dom0_ballooning:
 
254
            dominfo.alloc_mem = 0 
 
255
            raise VmError(('Not enough free memory and enable-dom0-ballooning '
 
256
                           'is False, so I cannot release any more.  '
 
257
                           'I need %d KiB but only have %d.') %
 
258
                          (need_mem, free_mem))
 
259
        elif new_alloc < dom0_min_mem:
 
260
            dominfo.alloc_mem = 0 
 
261
            raise VmError(
 
262
                ('I need %d KiB, but dom0_min_mem is %d and shrinking to '
 
263
                 '%d KiB would leave only %d KiB free.') %
 
264
                (need_mem, dom0_min_mem, dom0_min_mem,
 
265
                 free_mem + scrub_mem + dom0_alloc - dom0_min_mem))
 
266
        else:
 
267
            dom0_start_alloc_mb = get_dom0_current_alloc() / 1024
 
268
            dom0.setMemoryTarget(dom0_start_alloc_mb)
 
269
            dominfo.alloc_mem = 0 
 
270
            raise VmError(
 
271
                ('Not enough memory is available, and dom0 cannot'
 
272
                 ' be shrunk any further'))
 
273
 
 
274
    finally:
 
275
        # allow tmem to accept pages again
 
276
        xc.tmem_control(0,TMEMC_THAW,-1, 0, 0, 0, "")
 
277
        del xc