diff --git a/tools/ocaml/xenstored/domains.ml b/tools/ocaml/xenstored/domains.ml index 63c6ad5daa..25fd592244 100644 --- a/tools/ocaml/xenstored/domains.ml +++ b/tools/ocaml/xenstored/domains.ml @@ -35,8 +35,9 @@ type domains = { on_first_conflict_pause: unit -> unit; (* If config is set to use individual instead of aggregate conflict-rate-limiting, - we use this instead of the queues. *) - mutable n_paused: int; + we use these counts instead of the queues. The second one includes the first. *) + mutable n_paused: int; (* Number of domains with zero or negative credit *) + mutable n_penalised: int; (* Number of domains with less than maximum credit *) } let init eventchn on_first_conflict_pause = { @@ -46,6 +47,7 @@ let init eventchn on_first_conflict_pause = { doms_with_conflict_penalty = Queue.create (); on_first_conflict_pause = on_first_conflict_pause; n_paused = 0; + n_penalised = 0; } let del doms id = Hashtbl.remove doms.table id let exist doms id = Hashtbl.mem doms.table id @@ -53,6 +55,23 @@ let find doms id = Hashtbl.find doms.table id let number doms = Hashtbl.length doms.table let iter doms fct = Hashtbl.iter (fun _ b -> fct b) doms.table +let rec is_empty_queue q = + Queue.is_empty q || + if !(Queue.peek q) = None + then ( + ignore (Queue.pop q); + is_empty_queue q + ) else false + +let all_at_max_credit doms = + if !Define.conflict_rate_limit_is_aggregate + then + (* Check both becuase if burst limit is 1.0 then a domain can go straight + * from max-credit to paused without getting into the penalty queue. *) + is_empty_queue doms.doms_with_conflict_penalty + && is_empty_queue doms.doms_conflict_paused + else doms.n_penalised = 0 + (* Functions to handle queues of domains given that the domain might be deleted while in a queue. *) let push dom queue = Queue.push (ref (Some dom)) queue @@ -132,13 +151,16 @@ let decr_conflict_credit doms dom = let before = dom.Domain.conflict_credit in let after = max (-1.0) (before -. 1.0) in dom.Domain.conflict_credit <- after; + let newly_penalised = + before >= !Define.conflict_burst_limit + && after < !Define.conflict_burst_limit in + let newly_paused = before > 0.0 && after <= 0.0 in if !Define.conflict_rate_limit_is_aggregate then ( - if before >= !Define.conflict_burst_limit - && after < !Define.conflict_burst_limit + if newly_penalised && after > 0.0 then ( push dom doms.doms_with_conflict_penalty - ) else if before > 0.0 && after <= 0.0 + ) else if newly_paused then ( let first_pause = Queue.is_empty doms.doms_conflict_paused in push dom doms.doms_conflict_paused; @@ -146,9 +168,12 @@ let decr_conflict_credit doms dom = ) else ( (* The queues are correct already: no further action needed. *) ) - ) else if before > 0.0 && after <= 0.0 then ( - doms.n_paused <- doms.n_paused + 1; - if doms.n_paused = 1 then doms.on_first_conflict_pause () + ) else ( + if newly_penalised then doms.n_penalised <- doms.n_penalised + 1; + if newly_paused then ( + doms.n_paused <- doms.n_paused + 1; + if doms.n_paused = 1 then doms.on_first_conflict_pause () + ) ) (* Give one point of credit to one domain, and update the queues appropriately. *) @@ -177,9 +202,13 @@ let incr_conflict_credit doms = let before = dom.Domain.conflict_credit in let after = min (before +. 1.0) !Define.conflict_burst_limit in dom.Domain.conflict_credit <- after; + if before <= 0.0 && after > 0.0 - then doms.n_paused <- doms.n_paused - 1 + then doms.n_paused <- doms.n_paused - 1; + + if before < !Define.conflict_burst_limit + && after >= !Define.conflict_burst_limit + then doms.n_penalised <- doms.n_penalised - 1 in - (* Scope for optimisation (probably tiny): avoid iteration if all domains are at max credit *) - iter doms inc + if doms.n_penalised > 0 then iter doms inc ) diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml index c0097018e2..8c82fe94af 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -436,7 +436,10 @@ let _ = peaceful_mw; let start_time = Unix.gettimeofday () in let timeout = - let until_next_activity = min (max 0. (!next_frequent_ops -. start_time)) period_ops_interval in + let until_next_activity = + if Domains.all_at_max_credit domains + then period_ops_interval + else min (max 0. (!next_frequent_ops -. start_time)) period_ops_interval in if peaceful_mw <> [] then 0. else until_next_activity in let inset, outset = Connections.select ~only_if:is_peaceful cons in