4040CHECK_MB_S = 100
4141MIN_TOTAL_MEMORY_TRANSFER = 150 * 1024 * 1024
4242MIN_MEM_CHANGE_WHEN_UNDER_PREF = 15 * 1024 * 1024
43+ #: number of loop iterations for CHECK_PERIOD_S seconds
44+ CHECK_PERIOD = max (1 , int ((CHECK_PERIOD_S + 0.0 ) / BALLOON_DELAY ))
45+ #: number of free memory bytes expected to get during CHECK_PERIOD_S
46+ #: seconds
47+ CHECK_DELTA = CHECK_PERIOD_S * CHECK_MB_S * 1024 * 1024
4348
4449
4550class SystemState :
@@ -110,11 +115,13 @@ def get_free_xen_mem(self) -> int:
110115 )
111116 return xen_free - assigned_but_unused
112117
113- # Refresh information on memory assigned to all domains
114- def refresh_mem_actual (self ) -> None :
118+ # Refresh information on memory assigned to all or specific domains
119+ def refresh_mem_actual (self , domid_list : Optional [ list ] = None ) -> None :
115120 for domain in self .xc .domain_getinfo ():
116121 domid = str (domain ["domid" ])
117122 if domid in self .dom_dict :
123+ if domid_list and domid not in domid_list :
124+ continue
118125 dom = self .dom_dict [domid ]
119126 # Real memory usage
120127 dom .mem_current = domain ["mem_kb" ] * 1024
@@ -216,17 +223,12 @@ def do_balloon(self, mem_size) -> bool:
216223 for dom in self .dom_dict .values ():
217224 dom .no_progress = False
218225
219- #: number of loop iterations for CHECK_PERIOD_S seconds
220- check_period = max (1 , int ((CHECK_PERIOD_S + 0.0 ) / BALLOON_DELAY ))
221- #: number of free memory bytes expected to get during CHECK_PERIOD_S
222- #: seconds
223- check_delta = CHECK_PERIOD_S * CHECK_MB_S * 1024 * 1024
224226 #: helper array for holding free memory size, CHECK_PERIOD_S seconds
225227 #: ago, at every loop iteration
226- xenfree_ring = [0 ] * check_period
228+ xenfree_ring = [0 ] * CHECK_PERIOD
227229
228230 while True :
229- self .log .debug ("niter={:2d }" .format (niter ))
231+ self .log .debug ("niter={:d }" .format (niter ))
230232 self .refresh_mem_actual ()
231233 xenfree = self .get_free_xen_mem ()
232234 self .log .info ("xenfree={!r}" .format (xenfree ))
@@ -235,10 +237,10 @@ def do_balloon(self, mem_size) -> bool:
235237 return True
236238 # fail the request if over past CHECK_PERIOD_S seconds,
237239 # we got less than CHECK_MB_S MB/s on average
238- ring_slot = niter % check_period
240+ ring_slot = niter % CHECK_PERIOD
239241 if (
240- niter >= check_period
241- and xenfree < xenfree_ring [ring_slot ] + check_delta
242+ niter >= CHECK_PERIOD
243+ and xenfree < xenfree_ring [ring_slot ] + CHECK_DELTA
242244 ):
243245 return False
244246 xenfree_ring [ring_slot ] = xenfree
@@ -265,6 +267,56 @@ def do_balloon(self, mem_size) -> bool:
265267 time .sleep (BALLOON_DELAY )
266268 niter = niter + 1
267269
270+ def do_balloon_dom (self , dom_memset : dict ) -> bool :
271+ self .log .info ("do_balloon_dom(dom_memset={!r})" .format (dom_memset ))
272+ niter = 0
273+ if not dom_memset :
274+ return False
275+
276+ domid_list = list (dom_memset .keys ())
277+ dom_dict = {
278+ domid : state
279+ for domid , state in self .dom_dict .items ()
280+ if domid in domid_list
281+ }
282+
283+ for _ , dom in dom_dict .items ():
284+ dom .no_progress = False
285+
286+ memset_reqs = {}
287+ for domid , memset in dom_memset .items ():
288+ if memset == 0 :
289+ mem_pref = qubes .qmemman .algo .pref_mem (dom_dict [domid ])
290+ memset_reqs [domid ] = mem_pref
291+ self .log .debug (
292+ "mem for dom '%s' is 0, using its pref '%s'" ,
293+ domid ,
294+ mem_pref ,
295+ )
296+ else :
297+ memset_reqs [domid ] = memset
298+
299+ succeeded = []
300+ while True :
301+ self .log .debug ("niter={:d}" .format (niter ))
302+ self .refresh_mem_actual (domid_list )
303+ for domid , dom in dom_dict .items ():
304+ assert isinstance (dom .mem_actual , int )
305+ if (
306+ domid not in succeeded
307+ and dom .mem_actual / memset_reqs [domid ] < 1.1
308+ ):
309+ succeeded .append (domid )
310+ if all (dom in succeeded for dom in domid_list ):
311+ return True
312+ if niter >= CHECK_PERIOD :
313+ return False
314+ for domid , memset in memset_reqs .items ():
315+ self .mem_set (domid , memset )
316+ self .log .debug ("sleeping for {} s" .format (BALLOON_DELAY ))
317+ time .sleep (BALLOON_DELAY )
318+ niter += 1
319+
268320 def refresh_meminfo (self , domid , untrusted_meminfo_key ) -> None :
269321 self .log .debug (
270322 "refresh_meminfo(domid={}, untrusted_meminfo_key={!r})" .format (
0 commit comments