1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
From 374c6a67e3d139a04ecde127a63ce70d24ed7b45 Mon Sep 17 00:00:00 2001
From: Thomas Sanders <thomas.sanders@citrix.com>
Date: Fri, 24 Mar 2017 19:55:03 +0000
Subject: [PATCH 14/15] oxenstored: don't wake to issue no conflict-credit
In the main loop, when choosing the timeout for the select function
call, we were setting it so as to wake up to issue conflict-credit to
any domains that could accept it. When xenstore is idle, this would
mean waking up every 50ms (by default) to do no work. With this
commit, we check whether any domain is below its cap, and if not then
we set the timeout for longer (the same timeout as before the
conflict-protection feature was added).
Reported-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Thomas Sanders <thomas.sanders@citrix.com>
Reviewed-by: Jonathan Davies <jonathan.davies@citrix.com>
---
tools/ocaml/xenstored/domains.ml | 51 ++++++++++++++++++++++++++++++--------
tools/ocaml/xenstored/xenstored.ml | 5 +++-
2 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/tools/ocaml/xenstored/domains.ml b/tools/ocaml/xenstored/domains.ml
index 99f68c7..61d1e2e 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
@@ -130,13 +149,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;
@@ -144,9 +166,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. *)
@@ -175,9 +200,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 d5c50fd..06387a8 100644
--- a/tools/ocaml/xenstored/xenstored.ml
+++ b/tools/ocaml/xenstored/xenstored.ml
@@ -437,7 +437,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
--
2.1.4
|