2 * Throttle infrastructure tests
4 * Copyright Nodalink, SARL. 2013
7 * BenoƮt Canet <benoit.canet@irqsave.net>
9 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10 * See the COPYING.LIB file in the top-level directory.
15 #include "block/aio.h"
16 #include "qemu/throttle.h"
18 static AioContext
*ctx
;
19 static LeakyBucket bkt
;
20 static ThrottleConfig cfg
;
21 static ThrottleState ts
;
24 static bool double_cmp(double x
, double y
)
26 return fabsl(x
- y
) < 1e-6;
29 /* tests for single bucket operations */
30 static void test_leak_bucket(void)
32 /* set initial value */
37 /* leak an op work of time */
38 throttle_leak_bucket(&bkt
, NANOSECONDS_PER_SECOND
/ 150);
39 g_assert(bkt
.avg
== 150);
40 g_assert(bkt
.max
== 15);
41 g_assert(double_cmp(bkt
.level
, 0.5));
43 /* leak again emptying the bucket */
44 throttle_leak_bucket(&bkt
, NANOSECONDS_PER_SECOND
/ 150);
45 g_assert(bkt
.avg
== 150);
46 g_assert(bkt
.max
== 15);
47 g_assert(double_cmp(bkt
.level
, 0));
49 /* check that the bucket level won't go lower */
50 throttle_leak_bucket(&bkt
, NANOSECONDS_PER_SECOND
/ 150);
51 g_assert(bkt
.avg
== 150);
52 g_assert(bkt
.max
== 15);
53 g_assert(double_cmp(bkt
.level
, 0));
56 static void test_compute_wait(void)
61 /* no operation limit set */
65 wait
= throttle_compute_wait(&bkt
);
72 wait
= throttle_compute_wait(&bkt
);
75 /* below zero delta */
79 wait
= throttle_compute_wait(&bkt
);
82 /* half an operation above max */
86 wait
= throttle_compute_wait(&bkt
);
87 /* time required to do half an operation */
88 result
= (int64_t) NANOSECONDS_PER_SECOND
/ 150 / 2;
89 g_assert(wait
== result
);
92 /* functions to test ThrottleState initialization/destroy methods */
93 static void read_timer_cb(void *opaque
)
97 static void write_timer_cb(void *opaque
)
101 static void test_init(void)
105 /* fill the structure with crap */
106 memset(&ts
, 1, sizeof(ts
));
108 /* init the structure */
109 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
110 read_timer_cb
, write_timer_cb
, &ts
);
112 /* check initialized fields */
113 g_assert(ts
.clock_type
== QEMU_CLOCK_VIRTUAL
);
114 g_assert(ts
.timers
[0]);
115 g_assert(ts
.timers
[1]);
117 /* check other fields where cleared */
118 g_assert(!ts
.previous_leak
);
119 g_assert(!ts
.cfg
.op_size
);
120 for (i
= 0; i
< BUCKETS_COUNT
; i
++) {
121 g_assert(!ts
.cfg
.buckets
[i
].avg
);
122 g_assert(!ts
.cfg
.buckets
[i
].max
);
123 g_assert(!ts
.cfg
.buckets
[i
].level
);
126 throttle_destroy(&ts
);
129 static void test_destroy(void)
132 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
133 read_timer_cb
, write_timer_cb
, &ts
);
134 throttle_destroy(&ts
);
135 for (i
= 0; i
< 2; i
++) {
136 g_assert(!ts
.timers
[i
]);
140 /* function to test throttle_config and throttle_get_config */
141 static void test_config_functions(void)
144 ThrottleConfig orig_cfg
, final_cfg
;
146 orig_cfg
.buckets
[THROTTLE_BPS_TOTAL
].avg
= 153;
147 orig_cfg
.buckets
[THROTTLE_BPS_READ
].avg
= 56;
148 orig_cfg
.buckets
[THROTTLE_BPS_WRITE
].avg
= 1;
150 orig_cfg
.buckets
[THROTTLE_OPS_TOTAL
].avg
= 150;
151 orig_cfg
.buckets
[THROTTLE_OPS_READ
].avg
= 69;
152 orig_cfg
.buckets
[THROTTLE_OPS_WRITE
].avg
= 23;
154 orig_cfg
.buckets
[THROTTLE_BPS_TOTAL
].max
= 0; /* should be corrected */
155 orig_cfg
.buckets
[THROTTLE_BPS_READ
].max
= 1; /* should not be corrected */
156 orig_cfg
.buckets
[THROTTLE_BPS_WRITE
].max
= 120;
158 orig_cfg
.buckets
[THROTTLE_OPS_TOTAL
].max
= 150;
159 orig_cfg
.buckets
[THROTTLE_OPS_READ
].max
= 400;
160 orig_cfg
.buckets
[THROTTLE_OPS_WRITE
].max
= 500;
162 orig_cfg
.buckets
[THROTTLE_BPS_TOTAL
].level
= 45;
163 orig_cfg
.buckets
[THROTTLE_BPS_READ
].level
= 65;
164 orig_cfg
.buckets
[THROTTLE_BPS_WRITE
].level
= 23;
166 orig_cfg
.buckets
[THROTTLE_OPS_TOTAL
].level
= 1;
167 orig_cfg
.buckets
[THROTTLE_OPS_READ
].level
= 90;
168 orig_cfg
.buckets
[THROTTLE_OPS_WRITE
].level
= 75;
170 orig_cfg
.op_size
= 1;
172 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
173 read_timer_cb
, write_timer_cb
, &ts
);
174 /* structure reset by throttle_init previous_leak should be null */
175 g_assert(!ts
.previous_leak
);
176 throttle_config(&ts
, &orig_cfg
);
178 /* has previous leak been initialized by throttle_config ? */
179 g_assert(ts
.previous_leak
);
181 /* get back the fixed configuration */
182 throttle_get_config(&ts
, &final_cfg
);
184 throttle_destroy(&ts
);
186 g_assert(final_cfg
.buckets
[THROTTLE_BPS_TOTAL
].avg
== 153);
187 g_assert(final_cfg
.buckets
[THROTTLE_BPS_READ
].avg
== 56);
188 g_assert(final_cfg
.buckets
[THROTTLE_BPS_WRITE
].avg
== 1);
190 g_assert(final_cfg
.buckets
[THROTTLE_OPS_TOTAL
].avg
== 150);
191 g_assert(final_cfg
.buckets
[THROTTLE_OPS_READ
].avg
== 69);
192 g_assert(final_cfg
.buckets
[THROTTLE_OPS_WRITE
].avg
== 23);
194 g_assert(final_cfg
.buckets
[THROTTLE_BPS_TOTAL
].max
== 15.3);/* fixed */
195 g_assert(final_cfg
.buckets
[THROTTLE_BPS_READ
].max
== 1); /* not fixed */
196 g_assert(final_cfg
.buckets
[THROTTLE_BPS_WRITE
].max
== 120);
198 g_assert(final_cfg
.buckets
[THROTTLE_OPS_TOTAL
].max
== 150);
199 g_assert(final_cfg
.buckets
[THROTTLE_OPS_READ
].max
== 400);
200 g_assert(final_cfg
.buckets
[THROTTLE_OPS_WRITE
].max
== 500);
202 g_assert(final_cfg
.op_size
== 1);
204 /* check bucket have been cleared */
205 for (i
= 0; i
< BUCKETS_COUNT
; i
++) {
206 g_assert(!final_cfg
.buckets
[i
].level
);
210 /* functions to test is throttle is enabled by a config */
211 static void set_cfg_value(bool is_max
, int index
, int value
)
214 cfg
.buckets
[index
].max
= value
;
216 cfg
.buckets
[index
].avg
= value
;
220 static void test_enabled(void)
224 memset(&cfg
, 0, sizeof(cfg
));
225 g_assert(!throttle_enabled(&cfg
));
227 for (i
= 0; i
< BUCKETS_COUNT
; i
++) {
228 memset(&cfg
, 0, sizeof(cfg
));
229 set_cfg_value(false, i
, 150);
230 g_assert(throttle_enabled(&cfg
));
233 for (i
= 0; i
< BUCKETS_COUNT
; i
++) {
234 memset(&cfg
, 0, sizeof(cfg
));
235 set_cfg_value(false, i
, -150);
236 g_assert(!throttle_enabled(&cfg
));
240 /* tests functions for throttle_conflicting */
242 static void test_conflicts_for_one_set(bool is_max
,
247 memset(&cfg
, 0, sizeof(cfg
));
248 g_assert(!throttle_conflicting(&cfg
));
250 set_cfg_value(is_max
, total
, 1);
251 set_cfg_value(is_max
, read
, 1);
252 g_assert(throttle_conflicting(&cfg
));
254 memset(&cfg
, 0, sizeof(cfg
));
255 set_cfg_value(is_max
, total
, 1);
256 set_cfg_value(is_max
, write
, 1);
257 g_assert(throttle_conflicting(&cfg
));
259 memset(&cfg
, 0, sizeof(cfg
));
260 set_cfg_value(is_max
, total
, 1);
261 set_cfg_value(is_max
, read
, 1);
262 set_cfg_value(is_max
, write
, 1);
263 g_assert(throttle_conflicting(&cfg
));
265 memset(&cfg
, 0, sizeof(cfg
));
266 set_cfg_value(is_max
, total
, 1);
267 g_assert(!throttle_conflicting(&cfg
));
269 memset(&cfg
, 0, sizeof(cfg
));
270 set_cfg_value(is_max
, read
, 1);
271 set_cfg_value(is_max
, write
, 1);
272 g_assert(!throttle_conflicting(&cfg
));
275 static void test_conflicting_config(void)
277 /* bps average conflicts */
278 test_conflicts_for_one_set(false,
283 /* ops average conflicts */
284 test_conflicts_for_one_set(false,
289 /* bps average conflicts */
290 test_conflicts_for_one_set(true,
294 /* ops average conflicts */
295 test_conflicts_for_one_set(true,
300 /* functions to test the throttle_is_valid function */
301 static void test_is_valid_for_value(int value
, bool should_be_valid
)
304 for (is_max
= 0; is_max
< 2; is_max
++) {
305 for (index
= 0; index
< BUCKETS_COUNT
; index
++) {
306 memset(&cfg
, 0, sizeof(cfg
));
307 set_cfg_value(is_max
, index
, value
);
308 g_assert(throttle_is_valid(&cfg
) == should_be_valid
);
313 static void test_is_valid(void)
315 /* negative number are invalid */
316 test_is_valid_for_value(-1, false);
317 /* zero are valids */
318 test_is_valid_for_value(0, true);
319 /* positives numers are valids */
320 test_is_valid_for_value(1, true);
323 static void test_have_timer(void)
325 /* zero the structure */
326 memset(&ts
, 0, sizeof(ts
));
328 /* no timer set should return false */
329 g_assert(!throttle_have_timer(&ts
));
331 /* init the structure */
332 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
333 read_timer_cb
, write_timer_cb
, &ts
);
335 /* timer set by init should return true */
336 g_assert(throttle_have_timer(&ts
));
338 throttle_destroy(&ts
);
341 static void test_detach_attach(void)
343 /* zero the structure */
344 memset(&ts
, 0, sizeof(ts
));
346 /* init the structure */
347 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
348 read_timer_cb
, write_timer_cb
, &ts
);
350 /* timer set by init should return true */
351 g_assert(throttle_have_timer(&ts
));
353 /* timer should no longer exist after detaching */
354 throttle_detach_aio_context(&ts
);
355 g_assert(!throttle_have_timer(&ts
));
357 /* timer should exist again after attaching */
358 throttle_attach_aio_context(&ts
, ctx
);
359 g_assert(throttle_have_timer(&ts
));
361 throttle_destroy(&ts
);
364 static bool do_test_accounting(bool is_ops
, /* are we testing bps or ops */
365 int size
, /* size of the operation to do */
366 double avg
, /* io limit */
367 uint64_t op_size
, /* ideal size of an io */
372 BucketType to_test
[2][3] = { { THROTTLE_BPS_TOTAL
,
374 THROTTLE_BPS_WRITE
, },
375 { THROTTLE_OPS_TOTAL
,
377 THROTTLE_OPS_WRITE
, } };
382 for (i
= 0; i
< 3; i
++) {
383 BucketType index
= to_test
[is_ops
][i
];
384 cfg
.buckets
[index
].avg
= avg
;
387 cfg
.op_size
= op_size
;
389 throttle_init(&ts
, ctx
, QEMU_CLOCK_VIRTUAL
,
390 read_timer_cb
, write_timer_cb
, &ts
);
391 throttle_config(&ts
, &cfg
);
394 throttle_account(&ts
, false, size
);
395 /* account a write */
396 throttle_account(&ts
, true, size
);
398 /* check total result */
399 index
= to_test
[is_ops
][0];
400 if (!double_cmp(ts
.cfg
.buckets
[index
].level
, total_result
)) {
404 /* check read result */
405 index
= to_test
[is_ops
][1];
406 if (!double_cmp(ts
.cfg
.buckets
[index
].level
, read_result
)) {
410 /* check write result */
411 index
= to_test
[is_ops
][2];
412 if (!double_cmp(ts
.cfg
.buckets
[index
].level
, write_result
)) {
416 throttle_destroy(&ts
);
421 static void test_accounting(void)
426 g_assert(do_test_accounting(false,
435 g_assert(do_test_accounting(false,
443 /* op of size 2 and orthogonal parameter change */
444 g_assert(do_test_accounting(false,
456 g_assert(do_test_accounting(true,
465 g_assert(do_test_accounting(true,
473 /* jumbo op accounting fragmentation : size 64 with op size of 13 units */
474 g_assert(do_test_accounting(true,
482 /* same with orthogonal parameters changes */
483 g_assert(do_test_accounting(true,
492 int main(int argc
, char **argv
)
498 ctx
= aio_context_new();
499 src
= aio_get_g_source(ctx
);
500 g_source_attach(src
, NULL
);
503 do {} while (g_main_context_iteration(NULL
, false));
505 /* tests in the same order as the header function declarations */
506 g_test_init(&argc
, &argv
, NULL
);
507 g_test_add_func("/throttle/leak_bucket", test_leak_bucket
);
508 g_test_add_func("/throttle/compute_wait", test_compute_wait
);
509 g_test_add_func("/throttle/init", test_init
);
510 g_test_add_func("/throttle/destroy", test_destroy
);
511 g_test_add_func("/throttle/have_timer", test_have_timer
);
512 g_test_add_func("/throttle/detach_attach", test_detach_attach
);
513 g_test_add_func("/throttle/config/enabled", test_enabled
);
514 g_test_add_func("/throttle/config/conflicting", test_conflicting_config
);
515 g_test_add_func("/throttle/config/is_valid", test_is_valid
);
516 g_test_add_func("/throttle/config_functions", test_config_functions
);
517 g_test_add_func("/throttle/accounting", test_accounting
);