qmemman_algo.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import string
  2. def parse_meminfo(meminfo):
  3. dict = {}
  4. l1 = string.split(meminfo,"\n")
  5. for i in l1:
  6. l2 = string.split(i)
  7. if len(l2) >= 2:
  8. dict[string.rstrip(l2[0], ":")] = l2[1]
  9. try:
  10. for i in ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree'):
  11. val = int(dict[i])*1024
  12. if (val < 0):
  13. return None
  14. dict[i] = val
  15. except:
  16. return None
  17. if dict['SwapTotal'] < dict['SwapFree']:
  18. return None
  19. return dict
  20. def is_suspicious(dom):
  21. ret = False
  22. if dom.meminfo['SwapTotal'] < dom.meminfo['SwapFree']:
  23. ret = True
  24. if dom.meminfo['MemTotal'] < dom.meminfo['MemFree'] + dom.meminfo['Cached'] + dom.meminfo['Buffers']:
  25. ret = True
  26. if ret:
  27. print 'suspicious meminfo for domain', dom.id, 'mem actual', dom.memory_actual, dom.meminfo
  28. return ret
  29. def refresh_meminfo_for_domain(dom, xenstore_key):
  30. meminfo = parse_meminfo(xenstore_key)
  31. dom.meminfo = meminfo
  32. if meminfo is None:
  33. return
  34. if is_suspicious(dom):
  35. dom.meminfo = None
  36. dom.mem_used = None
  37. else:
  38. dom.mem_used = dom.meminfo['MemTotal'] - dom.meminfo['MemFree'] - dom.meminfo['Cached'] - dom.meminfo['Buffers'] + dom.meminfo['SwapTotal'] - dom.meminfo['SwapFree']
  39. def prefmem(dom):
  40. CACHE_FACTOR = 1.3
  41. #dom0 is special, as it must have large cache, for vbds. Thus, give it a special boost
  42. if dom.id == '0':
  43. return dom.mem_used*CACHE_FACTOR + 350*1024*1024
  44. return dom.mem_used*CACHE_FACTOR
  45. def memneeded(dom):
  46. #do not change
  47. #in balance(), "distribute totalsum proportionally to mempref" relies on this exact formula
  48. ret = prefmem(dom) - dom.memory_actual
  49. return ret
  50. def balloon(memsize, domdict):
  51. REQ_SAFETY_NET_FACTOR = 1.05
  52. donors = list()
  53. request = list()
  54. available = 0
  55. for i in domdict.keys():
  56. if domdict[i].meminfo is None:
  57. continue
  58. if domdict[i].no_progress:
  59. continue
  60. need = memneeded(domdict[i])
  61. if need < 0:
  62. print 'balloon: dom' , i, 'has actual memory', domdict[i].memory_actual
  63. donors.append((i,-need))
  64. available-=need
  65. print 'req=', memsize, 'avail=', available, 'donors', donors
  66. if available<memsize:
  67. return ()
  68. scale = 1.0*memsize/available
  69. for donors_iter in donors:
  70. id, mem = donors_iter
  71. memborrowed = mem*scale*REQ_SAFETY_NET_FACTOR
  72. print 'borrow' , memborrowed, 'from', id
  73. memtarget = int(domdict[id].memory_actual - memborrowed)
  74. request.append((id, memtarget))
  75. return request
  76. # REQ_SAFETY_NET_FACTOR is a bit greater that 1. So that if the domain yields a bit less than requested, due
  77. # to e.g. rounding errors, we will not get stuck. The surplus will return to the VM during "balance" call.
  78. #redistribute positive "totalsum" of memory between domains, proportionally to prefmem
  79. def balance_when_enough_memory(domdict, xenfree, total_mem_pref, totalsum):
  80. donors_rq = list()
  81. acceptors_rq = list()
  82. for i in domdict.keys():
  83. if domdict[i].meminfo is None:
  84. continue
  85. #distribute totalsum proportionally to mempref
  86. scale = 1.0*prefmem(domdict[i])/total_mem_pref
  87. target_nonint = prefmem(domdict[i]) + scale*totalsum
  88. #prevent rounding errors
  89. target = int(0.999*target_nonint)
  90. if (target < domdict[i].memory_actual):
  91. donors_rq.append((i, target))
  92. else:
  93. acceptors_rq.append((i, target))
  94. # print 'balance(enough): xenfree=', xenfree, 'requests:', donors_rq + acceptors_rq
  95. return donors_rq + acceptors_rq
  96. #when not enough mem to make everyone be above prefmem, make donors be at prefmem, and
  97. #redistribute anything left between acceptors
  98. def balance_when_low_on_memory(domdict, xenfree, total_mem_pref_acceptors, donors, acceptors):
  99. donors_rq = list()
  100. acceptors_rq = list()
  101. squeezed_mem = xenfree
  102. for i in donors:
  103. avail = -memneeded(domdict[i])
  104. if avail < 10*1024*1024:
  105. #probably we have already tried making it exactly at prefmem, give up
  106. continue
  107. squeezed_mem -= avail
  108. donors_rq.append((i, prefmem(domdict[i])))
  109. #the below can happen if initially xen free memory is below 50M
  110. if squeezed_mem < 0:
  111. return donors_rq
  112. for i in acceptors:
  113. scale = 1.0*prefmem(domdict[i])/total_mem_pref_acceptors
  114. target_nonint = domdict[i].memory_actual + scale*squeezed_mem
  115. acceptors_rq.append((i, int(target_nonint)))
  116. # print 'balance(low): xenfree=', xenfree, 'requests:', donors_rq + acceptors_rq
  117. return donors_rq + acceptors_rq
  118. def balance(xenfree, domdict):
  119. total_memneeded = 0
  120. total_mem_pref = 0
  121. total_mem_pref_acceptors = 0
  122. donors = list()
  123. acceptors = list()
  124. #pass 1: compute the above "total" values
  125. for i in domdict.keys():
  126. if domdict[i].meminfo is None:
  127. continue
  128. need = memneeded(domdict[i])
  129. # print 'domain' , i, 'act/pref', domdict[i].memory_actual, prefmem(domdict[i]), 'need=', need
  130. if need < 0:
  131. donors.append(i)
  132. else:
  133. acceptors.append(i)
  134. total_mem_pref_acceptors += prefmem(domdict[i])
  135. total_memneeded += need
  136. total_mem_pref += prefmem(domdict[i])
  137. totalsum = xenfree - total_memneeded
  138. if totalsum > 0:
  139. return balance_when_enough_memory(domdict, xenfree, total_mem_pref, totalsum)
  140. else:
  141. return balance_when_low_on_memory(domdict, xenfree, total_mem_pref_acceptors, donors, acceptors)