3737CHECK_MB_S = 100
3838MIN_TOTAL_MEMORY_TRANSFER = 150 * 1024 * 1024
3939MIN_MEM_CHANGE_WHEN_UNDER_PREF = 15 * 1024 * 1024
40+ #: number of loop iterations for CHECK_PERIOD_S seconds
41+ CHECK_PERIOD = max (1 , int ((CHECK_PERIOD_S + 0.0 ) / BALLOON_DELAY ))
42+ #: number of free memory bytes expected to get during CHECK_PERIOD_S
43+ #: seconds
44+ CHECK_DELTA = CHECK_PERIOD_S * CHECK_MB_S * 1024 * 1024
4045
4146
4247class SystemState :
@@ -116,11 +121,13 @@ def get_free_xen_mem(self) -> int:
116121 )
117122 return xen_free - assigned_but_unused
118123
119- # Refresh information on memory assigned to all domains
120- def refresh_mem_actual (self ) -> None :
124+ # Refresh information on memory assigned to all or specific domains
125+ def refresh_mem_actual (self , domid_list : Optional [ list ] = None ) -> None :
121126 for domain in self .xc .domain_getinfo ():
122127 domid = str (domain ["domid" ])
123128 if domid in self .dom_dict :
129+ if domid_list and domid not in domid_list :
130+ continue
124131 dom = self .dom_dict [domid ]
125132 # Real memory usage
126133 dom .mem_current = domain ["mem_kb" ] * 1024
@@ -220,17 +227,12 @@ def do_balloon(self, mem_size) -> bool:
220227 for _ , dom in self .dom_dict .items ():
221228 dom .no_progress = False
222229
223- #: number of loop iterations for CHECK_PERIOD_S seconds
224- check_period = max (1 , int ((CHECK_PERIOD_S + 0.0 ) / BALLOON_DELAY ))
225- #: number of free memory bytes expected to get during CHECK_PERIOD_S
226- #: seconds
227- check_delta = CHECK_PERIOD_S * CHECK_MB_S * 1024 * 1024
228230 #: helper array for holding free memory size, CHECK_PERIOD_S seconds
229231 #: ago, at every loop iteration
230- xenfree_ring = [0 ] * check_period
232+ xenfree_ring = [0 ] * CHECK_PERIOD
231233
232234 while True :
233- self .log .debug ("niter={:2d }" .format (niter ))
235+ self .log .debug ("niter={:d }" .format (niter ))
234236 self .refresh_mem_actual ()
235237 xenfree = self .get_free_xen_mem ()
236238 self .log .info ("xenfree={!r}" .format (xenfree ))
@@ -239,10 +241,10 @@ def do_balloon(self, mem_size) -> bool:
239241 return True
240242 # fail the request if over past CHECK_PERIOD_S seconds,
241243 # we got less than CHECK_MB_S MB/s on average
242- ring_slot = niter % check_period
244+ ring_slot = niter % CHECK_PERIOD
243245 if (
244- niter >= check_period
245- and xenfree < xenfree_ring [ring_slot ] + check_delta
246+ niter >= CHECK_PERIOD
247+ and xenfree < xenfree_ring [ring_slot ] + CHECK_DELTA
246248 ):
247249 return False
248250 xenfree_ring [ring_slot ] = xenfree
@@ -269,6 +271,56 @@ def do_balloon(self, mem_size) -> bool:
269271 time .sleep (BALLOON_DELAY )
270272 niter = niter + 1
271273
274+ def do_balloon_dom (self , dom_memset : dict ) -> bool :
275+ self .log .info ("do_balloon_dom(dom_memset={!r})" .format (dom_memset ))
276+ niter = 0
277+ if not dom_memset :
278+ return False
279+
280+ domid_list = list (dom_memset .keys ())
281+ dom_dict = {
282+ domid : state
283+ for domid , state in self .dom_dict .items ()
284+ if domid in domid_list
285+ }
286+
287+ for _ , dom in dom_dict .items ():
288+ dom .no_progress = False
289+
290+ memset_reqs = {}
291+ for domid , memset in dom_memset .items ():
292+ if memset == 0 :
293+ mem_pref = qubes .qmemman .algo .pref_mem (dom_dict [domid ])
294+ memset_reqs [domid ] = mem_pref
295+ self .log .debug (
296+ "mem for dom '%s' is 0, using its pref '%s'" ,
297+ domid ,
298+ mem_pref ,
299+ )
300+ else :
301+ memset_reqs [domid ] = memset
302+
303+ succeeded = []
304+ while True :
305+ self .log .debug ("niter={:d}" .format (niter ))
306+ self .refresh_mem_actual (domid_list )
307+ for domid , dom in dom_dict .items ():
308+ assert isinstance (dom .mem_actual , int )
309+ if (
310+ domid not in succeeded
311+ and dom .mem_actual / memset_reqs [domid ] < 1.1
312+ ):
313+ succeeded .append (domid )
314+ if all (dom in succeeded for dom in domid_list ):
315+ return True
316+ if niter >= CHECK_PERIOD :
317+ return False
318+ for domid , memset in memset_reqs .items ():
319+ self .mem_set (domid , memset )
320+ self .log .debug ("sleeping for {} s" .format (BALLOON_DELAY ))
321+ time .sleep (BALLOON_DELAY )
322+ niter += 1
323+
272324 def refresh_meminfo (self , domid , untrusted_meminfo_key ) -> None :
273325 self .log .debug (
274326 "refresh_meminfo(domid={}, untrusted_meminfo_key={!r})" .format (
0 commit comments