direct-io.hg

changeset 8277:8e74f2cf985e

Loop retrying when ballooning out, even when the dom0-min-mem setting means
that there is not sufficient memory available at the moment. Memory may be
about to be freed up by the system after a domain destruction (i.e. the memory
is being scrubbed asynchronously, and will be released soon).

Closes bug #407, bug #429.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Thu Dec 08 12:13:06 2005 +0000 (2005-12-08)
parents cdf76916951a
children 669b6252deee
files tools/python/xen/xend/balloon.py
line diff
     1.1 --- a/tools/python/xen/xend/balloon.py	Thu Dec 08 12:10:22 2005 +0000
     1.2 +++ b/tools/python/xen/xend/balloon.py	Thu Dec 08 12:13:06 2005 +0000
     1.3 @@ -30,52 +30,95 @@ from XendError import VmError
     1.4  PROC_XEN_BALLOON = "/proc/xen/balloon"
     1.5  BALLOON_OUT_SLACK = 1 # MiB.  We need this because the physinfo details are
     1.6                        # rounded.
     1.7 +RETRY_LIMIT = 10
     1.8 +##
     1.9 +# The time to sleep between retries grows linearly, using this value (in
    1.10 +# seconds).  When the system is lightly loaded, memory should be scrubbed and
    1.11 +# returned to the system very quickly, whereas when it is loaded, the system
    1.12 +# needs idle time to get the scrubbing done.  This linear growth accommodates
    1.13 +# such requirements.
    1.14 +SLEEP_TIME_GROWTH = 0.1
    1.15  
    1.16  
    1.17  def free(required):
    1.18      """Balloon out memory from the privileged domain so that there is the
    1.19      specified required amount (in KiB) free.
    1.20      """
    1.21 -    
    1.22 +
    1.23 +    # We check whether there is enough free memory, and if not, instruct dom0
    1.24 +    # to balloon out to free some up.  Memory freed by a destroyed domain may
    1.25 +    # not appear in the free_memory field immediately, because it needs to be
    1.26 +    # scrubbed before it can be released to the free list, which is done
    1.27 +    # asynchronously by Xen; ballooning is asynchronous also.  No matter where
    1.28 +    # we expect the free memory to come from, therefore, we need to wait for
    1.29 +    # it to become available.
    1.30 +    #
    1.31 +    # We are not allowed to balloon below dom0_min_mem, or if dom0_min_mem
    1.32 +    # is 0, we cannot balloon at all.  Memory can still become available
    1.33 +    # through a rebooting domain, however.
    1.34 +    #
    1.35 +    # Eventually, we time out (presumably because there really isn't enough
    1.36 +    # free memory).
    1.37 +    #
    1.38 +    # We don't want to set the memory target (triggering a watch) when that
    1.39 +    # has already been done, but we do want to respond to changing memory
    1.40 +    # usage, so we recheck the required alloc each time around the loop, but
    1.41 +    # track the last used value so that we don't trigger too many watches.
    1.42 +
    1.43 +    need_mem = (required + 1023) / 1024 + BALLOON_OUT_SLACK
    1.44 +
    1.45 +    xroot = XendRoot.instance()
    1.46      xc = xen.lowlevel.xc.xc()
    1.47 -    xroot = XendRoot.instance()
    1.48  
    1.49      try:
    1.50 -        free_mem = xc.physinfo()['free_memory']
    1.51 -        need_mem = (required + 1023) / 1024 + BALLOON_OUT_SLACK
    1.52 +        dom0_min_mem = xroot.get_dom0_min_mem()
    1.53 +
    1.54 +        retries = 0
    1.55 +        sleep_time = SLEEP_TIME_GROWTH
    1.56 +        last_new_alloc = None
    1.57 +        while retries < RETRY_LIMIT:
    1.58 +            free_mem = xc.physinfo()['free_memory']
    1.59  
    1.60 -        log.debug("Balloon: free %d; need %d.", free_mem, need_mem)
    1.61 -        
    1.62 -        if free_mem >= need_mem:
    1.63 -            return
    1.64 +            if free_mem >= need_mem:
    1.65 +                log.debug("Balloon: free %d; need %d; done.", free_mem,
    1.66 +                          need_mem)
    1.67 +                return
    1.68 +
    1.69 +            if retries == 0:
    1.70 +                log.debug("Balloon: free %d; need %d.", free_mem, need_mem)
    1.71 +
    1.72 +            if dom0_min_mem > 0:
    1.73 +                dom0_alloc = _get_dom0_alloc()
    1.74 +                new_alloc = dom0_alloc - (need_mem - free_mem)
    1.75  
    1.76 -        dom0_min_mem = xroot.get_dom0_min_mem()
    1.77 -        if dom0_min_mem == 0:
    1.78 -            raise VmError('Not enough free memory and dom0_min_mem is 0.')
    1.79 +                if (new_alloc >= dom0_min_mem and
    1.80 +                    new_alloc != last_new_alloc):
    1.81 +                    log.debug("Balloon: setting dom0 target to %d.",
    1.82 +                              new_alloc)
    1.83 +                    dom0 = XendDomain.instance().privilegedDomain()
    1.84 +                    dom0.setMemoryTarget(new_alloc)
    1.85 +                    last_new_alloc = new_alloc
    1.86 +                    # Continue to retry, waiting for ballooning.
    1.87  
    1.88 -        dom0_alloc = _get_dom0_alloc()
    1.89 -        dom0_new_alloc = dom0_alloc - (need_mem - free_mem)
    1.90 -        if dom0_new_alloc < dom0_min_mem:
    1.91 +            time.sleep(sleep_time)
    1.92 +            retries += 1
    1.93 +            sleep_time += SLEEP_TIME_GROWTH
    1.94 +
    1.95 +        # Not enough memory; diagnose the problem.
    1.96 +        if dom0_min_mem == 0:
    1.97 +            raise VmError(('Not enough free memory and dom0_min_mem is 0, so '
    1.98 +                           'I cannot release any more.  I need %d MiB but '
    1.99 +                           'only have %d.') %
   1.100 +                          (need_mem, free_mem))
   1.101 +        elif new_alloc >= dom0_min_mem:
   1.102              raise VmError(
   1.103                  ('I need %d MiB, but dom0_min_mem is %d and shrinking to '
   1.104                   '%d MiB would leave only %d MiB free.') %
   1.105                  (need_mem, dom0_min_mem, dom0_min_mem,
   1.106 -                 free_mem + (dom0_alloc - dom0_min_mem)))
   1.107 -
   1.108 -        dom0 = XendDomain.instance().privilegedDomain()
   1.109 -        dom0.setMemoryTarget(dom0_new_alloc)
   1.110 +                 free_mem + dom0_alloc - dom0_min_mem))
   1.111 +        else:
   1.112 +            raise VmError('The privileged domain did not balloon!')
   1.113  
   1.114 -        timeout = 20 # 2 sec
   1.115 -        while timeout > 0:
   1.116 -            time.sleep(0.1)
   1.117 -
   1.118 -            free_mem = xc.physinfo()['free_memory']
   1.119 -            if free_mem >= need_mem:
   1.120 -                return
   1.121 -
   1.122 -            timeout -= 1
   1.123 -
   1.124 -        raise VmError('The privileged domain did not balloon!')
   1.125      finally:
   1.126          del xc
   1.127