2 * Throttle infrastructure tests
4 * Copyright Nodalink, SARL. 2013
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"
17 #include "qemu/error-report.h"
19 static AioContext *ctx;
20 static LeakyBucket bkt;
21 static ThrottleConfig cfg;
22 static ThrottleState ts;
23 static ThrottleTimers tt;
26 static bool double_cmp(double x, double y)
28 return fabsl(x - y) < 1e-6;
31 /* tests for single bucket operations */
32 static void test_leak_bucket(void)
34 /* set initial value */
39 /* leak an op work of time */
40 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
41 g_assert(bkt.avg == 150);
42 g_assert(bkt.max == 15);
43 g_assert(double_cmp(bkt.level, 0.5));
45 /* leak again emptying the bucket */
46 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
47 g_assert(bkt.avg == 150);
48 g_assert(bkt.max == 15);
49 g_assert(double_cmp(bkt.level, 0));
51 /* check that the bucket level won't go lower */
52 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
53 g_assert(bkt.avg == 150);
54 g_assert(bkt.max == 15);
55 g_assert(double_cmp(bkt.level, 0));
58 static void test_compute_wait(void)
63 /* no operation limit set */
67 wait = throttle_compute_wait(&bkt);
74 wait = throttle_compute_wait(&bkt);
77 /* below zero delta */
81 wait = throttle_compute_wait(&bkt);
84 /* half an operation above max */
88 wait = throttle_compute_wait(&bkt);
89 /* time required to do half an operation */
90 result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2;
91 g_assert(wait == result);
94 /* functions to test ThrottleState initialization/destroy methods */
95 static void read_timer_cb(void *opaque)
99 static void write_timer_cb(void *opaque)
103 static void test_init(void)
107 /* fill the structures with crap */
108 memset(&ts, 1, sizeof(ts));
109 memset(&tt, 1, sizeof(tt));
111 /* init structures */
113 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
114 read_timer_cb, write_timer_cb, &ts);
116 /* check initialized fields */
117 g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
118 g_assert(tt.timers[0]);
119 g_assert(tt.timers[1]);
121 /* check other fields where cleared */
122 g_assert(!ts.previous_leak);
123 g_assert(!ts.cfg.op_size);
124 for (i = 0; i < BUCKETS_COUNT; i++) {
125 g_assert(!ts.cfg.buckets[i].avg);
126 g_assert(!ts.cfg.buckets[i].max);
127 g_assert(!ts.cfg.buckets[i].level);
130 throttle_timers_destroy(&tt);
133 static void test_destroy(void)
137 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
138 read_timer_cb, write_timer_cb, &ts);
139 throttle_timers_destroy(&tt);
140 for (i = 0; i < 2; i++) {
141 g_assert(!tt.timers[i]);
145 /* function to test throttle_config and throttle_get_config */
146 static void test_config_functions(void)
149 ThrottleConfig orig_cfg, final_cfg;
151 orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153;
152 orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56;
153 orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1;
155 orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150;
156 orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69;
157 orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23;
159 orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; /* should be corrected */
160 orig_cfg.buckets[THROTTLE_BPS_READ].max = 1; /* should not be corrected */
161 orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120;
163 orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150;
164 orig_cfg.buckets[THROTTLE_OPS_READ].max = 400;
165 orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500;
167 orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45;
168 orig_cfg.buckets[THROTTLE_BPS_READ].level = 65;
169 orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23;
171 orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1;
172 orig_cfg.buckets[THROTTLE_OPS_READ].level = 90;
173 orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75;
175 orig_cfg.op_size = 1;
178 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
179 read_timer_cb, write_timer_cb, &ts);
180 /* structure reset by throttle_init previous_leak should be null */
181 g_assert(!ts.previous_leak);
182 throttle_config(&ts, &tt, &orig_cfg);
184 /* has previous leak been initialized by throttle_config ? */
185 g_assert(ts.previous_leak);
187 /* get back the fixed configuration */
188 throttle_get_config(&ts, &final_cfg);
190 throttle_timers_destroy(&tt);
192 g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
193 g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
194 g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1);
196 g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150);
197 g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69);
198 g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23);
200 g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 15.3);/* fixed */
201 g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 1); /* not fixed */
202 g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120);
204 g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150);
205 g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400);
206 g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500);
208 g_assert(final_cfg.op_size == 1);
210 /* check bucket have been cleared */
211 for (i = 0; i < BUCKETS_COUNT; i++) {
212 g_assert(!final_cfg.buckets[i].level);
216 /* functions to test is throttle is enabled by a config */
217 static void set_cfg_value(bool is_max, int index, int value)
220 cfg.buckets[index].max = value;
222 cfg.buckets[index].avg = value;
226 static void test_enabled(void)
230 memset(&cfg, 0, sizeof(cfg));
231 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));
239 for (i = 0; i < BUCKETS_COUNT; i++) {
240 memset(&cfg, 0, sizeof(cfg));
241 set_cfg_value(false, i, -150);
242 g_assert(!throttle_enabled(&cfg));
246 /* tests functions for throttle_conflicting */
248 static void test_conflicts_for_one_set(bool is_max,
253 memset(&cfg, 0, sizeof(cfg));
254 g_assert(!throttle_conflicting(&cfg));
256 set_cfg_value(is_max, total, 1);
257 set_cfg_value(is_max, read, 1);
258 g_assert(throttle_conflicting(&cfg));
260 memset(&cfg, 0, sizeof(cfg));
261 set_cfg_value(is_max, total, 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 set_cfg_value(is_max, read, 1);
268 set_cfg_value(is_max, write, 1);
269 g_assert(throttle_conflicting(&cfg));
271 memset(&cfg, 0, sizeof(cfg));
272 set_cfg_value(is_max, total, 1);
273 g_assert(!throttle_conflicting(&cfg));
275 memset(&cfg, 0, sizeof(cfg));
276 set_cfg_value(is_max, read, 1);
277 set_cfg_value(is_max, write, 1);
278 g_assert(!throttle_conflicting(&cfg));
281 static void test_conflicting_config(void)
283 /* bps average conflicts */
284 test_conflicts_for_one_set(false,
289 /* ops average conflicts */
290 test_conflicts_for_one_set(false,
295 /* bps average conflicts */
296 test_conflicts_for_one_set(true,
300 /* ops average conflicts */
301 test_conflicts_for_one_set(true,
306 /* functions to test the throttle_is_valid function */
307 static void test_is_valid_for_value(int value, bool should_be_valid)
310 for (is_max = 0; is_max < 2; is_max++) {
311 for (index = 0; index < BUCKETS_COUNT; index++) {
312 memset(&cfg, 0, sizeof(cfg));
313 set_cfg_value(is_max, index, value);
314 g_assert(throttle_is_valid(&cfg) == should_be_valid);
319 static void test_is_valid(void)
321 /* negative number are invalid */
322 test_is_valid_for_value(-1, false);
323 /* zero are valids */
324 test_is_valid_for_value(0, true);
325 /* positives numers are valids */
326 test_is_valid_for_value(1, true);
329 static void test_have_timer(void)
331 /* zero structures */
332 memset(&ts, 0, sizeof(ts));
333 memset(&tt, 0, sizeof(tt));
335 /* no timer set should return false */
336 g_assert(!throttle_timers_are_initialized(&tt));
338 /* init structures */
340 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
341 read_timer_cb, write_timer_cb, &ts);
343 /* timer set by init should return true */
344 g_assert(throttle_timers_are_initialized(&tt));
346 throttle_timers_destroy(&tt);
349 static void test_detach_attach(void)
351 /* zero structures */
352 memset(&ts, 0, sizeof(ts));
353 memset(&tt, 0, sizeof(tt));
355 /* init the structure */
357 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
358 read_timer_cb, write_timer_cb, &ts);
360 /* timer set by init should return true */
361 g_assert(throttle_timers_are_initialized(&tt));
363 /* timer should no longer exist after detaching */
364 throttle_timers_detach_aio_context(&tt);
365 g_assert(!throttle_timers_are_initialized(&tt));
367 /* timer should exist again after attaching */
368 throttle_timers_attach_aio_context(&tt, ctx);
369 g_assert(throttle_timers_are_initialized(&tt));
371 throttle_timers_destroy(&tt);
374 static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
375 int size, /* size of the operation to do */
376 double avg, /* io limit */
377 uint64_t op_size, /* ideal size of an io */
382 BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
384 THROTTLE_BPS_WRITE, },
385 { THROTTLE_OPS_TOTAL,
387 THROTTLE_OPS_WRITE, } };
392 for (i = 0; i < 3; i++) {
393 BucketType index = to_test[is_ops][i];
394 cfg.buckets[index].avg = avg;
397 cfg.op_size = op_size;
400 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
401 read_timer_cb, write_timer_cb, &ts);
402 throttle_config(&ts, &tt, &cfg);
405 throttle_account(&ts, false, size);
406 /* account a write */
407 throttle_account(&ts, true, size);
409 /* check total result */
410 index = to_test[is_ops][0];
411 if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
415 /* check read result */
416 index = to_test[is_ops][1];
417 if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
421 /* check write result */
422 index = to_test[is_ops][2];
423 if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
427 throttle_timers_destroy(&tt);
432 static void test_accounting(void)
437 g_assert(do_test_accounting(false,
446 g_assert(do_test_accounting(false,
454 /* op of size 2 and orthogonal parameter change */
455 g_assert(do_test_accounting(false,
467 g_assert(do_test_accounting(true,
476 g_assert(do_test_accounting(true,
484 /* jumbo op accounting fragmentation : size 64 with op size of 13 units */
485 g_assert(do_test_accounting(true,
493 /* same with orthogonal parameters changes */
494 g_assert(do_test_accounting(true,
503 int main(int argc, char **argv)
506 Error *local_error = NULL;
510 ctx = aio_context_new(&local_error);
512 error_report("Failed to create AIO Context: '%s'",
513 error_get_pretty(local_error));
514 error_free(local_error);
517 src = aio_get_g_source(ctx);
518 g_source_attach(src, NULL);
521 do {} while (g_main_context_iteration(NULL, false));
523 /* tests in the same order as the header function declarations */
524 g_test_init(&argc, &argv, NULL);
525 g_test_add_func("/throttle/leak_bucket", test_leak_bucket);
526 g_test_add_func("/throttle/compute_wait", test_compute_wait);
527 g_test_add_func("/throttle/init", test_init);
528 g_test_add_func("/throttle/destroy", test_destroy);
529 g_test_add_func("/throttle/have_timer", test_have_timer);
530 g_test_add_func("/throttle/detach_attach", test_detach_attach);
531 g_test_add_func("/throttle/config/enabled", test_enabled);
532 g_test_add_func("/throttle/config/conflicting", test_conflicting_config);
533 g_test_add_func("/throttle/config/is_valid", test_is_valid);
534 g_test_add_func("/throttle/config_functions", test_config_functions);
535 g_test_add_func("/throttle/accounting", test_accounting);