]> git.proxmox.com Git - mirror_qemu.git/blame - tests/test-throttle.c
throttle: Set always an average value when setting a maximum value
[mirror_qemu.git] / tests / test-throttle.c
CommitLineData
f17cfe81
BC
1/*
2 * Throttle infrastructure tests
3 *
1fee955f
AG
4 * Copyright Nodalink, EURL. 2013-2014
5 * Copyright Igalia, S.L. 2015
f17cfe81
BC
6 *
7 * Authors:
1fee955f
AG
8 * Benoît Canet <benoit.canet@nodalink.com>
9 * Alberto Garcia <berto@igalia.com>
f17cfe81
BC
10 *
11 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
12 * See the COPYING.LIB file in the top-level directory.
13 */
14
681c28a3 15#include "qemu/osdep.h"
f17cfe81
BC
16#include <glib.h>
17#include <math.h>
13af91eb 18#include "block/aio.h"
f17cfe81 19#include "qemu/throttle.h"
2f78e491 20#include "qemu/error-report.h"
1fee955f 21#include "block/throttle-groups.h"
f17cfe81 22
748bfb4e
SW
23static AioContext *ctx;
24static LeakyBucket bkt;
25static ThrottleConfig cfg;
26static ThrottleState ts;
0e5b0a2d 27static ThrottleTimers tt;
f17cfe81 28
73f395fa 29/* useful function */
f17cfe81
BC
30static bool double_cmp(double x, double y)
31{
32 return fabsl(x - y) < 1e-6;
33}
34
35/* tests for single bucket operations */
36static void test_leak_bucket(void)
37{
38 /* set initial value */
39 bkt.avg = 150;
40 bkt.max = 15;
41 bkt.level = 1.5;
42
43 /* leak an op work of time */
13566fe3 44 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
f17cfe81
BC
45 g_assert(bkt.avg == 150);
46 g_assert(bkt.max == 15);
47 g_assert(double_cmp(bkt.level, 0.5));
48
49 /* leak again emptying the bucket */
13566fe3 50 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
f17cfe81
BC
51 g_assert(bkt.avg == 150);
52 g_assert(bkt.max == 15);
53 g_assert(double_cmp(bkt.level, 0));
54
55 /* check that the bucket level won't go lower */
13566fe3 56 throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
f17cfe81
BC
57 g_assert(bkt.avg == 150);
58 g_assert(bkt.max == 15);
59 g_assert(double_cmp(bkt.level, 0));
60}
61
62static void test_compute_wait(void)
63{
64 int64_t wait;
65 int64_t result;
66
67 /* no operation limit set */
68 bkt.avg = 0;
69 bkt.max = 15;
70 bkt.level = 1.5;
71 wait = throttle_compute_wait(&bkt);
72 g_assert(!wait);
73
74 /* zero delta */
75 bkt.avg = 150;
76 bkt.max = 15;
77 bkt.level = 15;
78 wait = throttle_compute_wait(&bkt);
79 g_assert(!wait);
80
81 /* below zero delta */
82 bkt.avg = 150;
83 bkt.max = 15;
84 bkt.level = 9;
85 wait = throttle_compute_wait(&bkt);
86 g_assert(!wait);
87
88 /* half an operation above max */
89 bkt.avg = 150;
90 bkt.max = 15;
91 bkt.level = 15.5;
92 wait = throttle_compute_wait(&bkt);
93 /* time required to do half an operation */
13566fe3 94 result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2;
f17cfe81
BC
95 g_assert(wait == result);
96}
97
98/* functions to test ThrottleState initialization/destroy methods */
99static void read_timer_cb(void *opaque)
100{
101}
102
103static void write_timer_cb(void *opaque)
104{
105}
106
107static void test_init(void)
108{
109 int i;
110
0e5b0a2d 111 /* fill the structures with crap */
f17cfe81 112 memset(&ts, 1, sizeof(ts));
0e5b0a2d 113 memset(&tt, 1, sizeof(tt));
f17cfe81 114
0e5b0a2d
BC
115 /* init structures */
116 throttle_init(&ts);
117 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
118 read_timer_cb, write_timer_cb, &ts);
f17cfe81
BC
119
120 /* check initialized fields */
0e5b0a2d
BC
121 g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
122 g_assert(tt.timers[0]);
123 g_assert(tt.timers[1]);
f17cfe81
BC
124
125 /* check other fields where cleared */
126 g_assert(!ts.previous_leak);
127 g_assert(!ts.cfg.op_size);
128 for (i = 0; i < BUCKETS_COUNT; i++) {
129 g_assert(!ts.cfg.buckets[i].avg);
130 g_assert(!ts.cfg.buckets[i].max);
131 g_assert(!ts.cfg.buckets[i].level);
132 }
133
0e5b0a2d 134 throttle_timers_destroy(&tt);
f17cfe81
BC
135}
136
137static void test_destroy(void)
138{
139 int i;
0e5b0a2d
BC
140 throttle_init(&ts);
141 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
142 read_timer_cb, write_timer_cb, &ts);
143 throttle_timers_destroy(&tt);
f17cfe81 144 for (i = 0; i < 2; i++) {
0e5b0a2d 145 g_assert(!tt.timers[i]);
f17cfe81
BC
146 }
147}
148
149/* function to test throttle_config and throttle_get_config */
150static void test_config_functions(void)
151{
152 int i;
153 ThrottleConfig orig_cfg, final_cfg;
154
155 orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153;
156 orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56;
157 orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1;
158
159 orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150;
160 orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69;
161 orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23;
162
163 orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; /* should be corrected */
164 orig_cfg.buckets[THROTTLE_BPS_READ].max = 1; /* should not be corrected */
165 orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120;
166
167 orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150;
168 orig_cfg.buckets[THROTTLE_OPS_READ].max = 400;
169 orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500;
170
171 orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45;
172 orig_cfg.buckets[THROTTLE_BPS_READ].level = 65;
173 orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23;
174
175 orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1;
176 orig_cfg.buckets[THROTTLE_OPS_READ].level = 90;
177 orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75;
178
179 orig_cfg.op_size = 1;
180
0e5b0a2d
BC
181 throttle_init(&ts);
182 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
183 read_timer_cb, write_timer_cb, &ts);
f17cfe81
BC
184 /* structure reset by throttle_init previous_leak should be null */
185 g_assert(!ts.previous_leak);
0e5b0a2d 186 throttle_config(&ts, &tt, &orig_cfg);
f17cfe81
BC
187
188 /* has previous leak been initialized by throttle_config ? */
189 g_assert(ts.previous_leak);
190
191 /* get back the fixed configuration */
192 throttle_get_config(&ts, &final_cfg);
193
0e5b0a2d 194 throttle_timers_destroy(&tt);
f17cfe81
BC
195
196 g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
197 g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
198 g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1);
199
200 g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150);
201 g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69);
202 g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23);
203
204 g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 15.3);/* fixed */
205 g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 1); /* not fixed */
206 g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120);
207
208 g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150);
209 g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400);
210 g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500);
211
212 g_assert(final_cfg.op_size == 1);
213
214 /* check bucket have been cleared */
215 for (i = 0; i < BUCKETS_COUNT; i++) {
216 g_assert(!final_cfg.buckets[i].level);
217 }
218}
219
220/* functions to test is throttle is enabled by a config */
221static void set_cfg_value(bool is_max, int index, int value)
222{
223 if (is_max) {
224 cfg.buckets[index].max = value;
6f9b6d57
AG
225 /* If max is set, avg should never be 0 */
226 cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1);
f17cfe81
BC
227 } else {
228 cfg.buckets[index].avg = value;
229 }
230}
231
232static void test_enabled(void)
233{
234 int i;
235
236 memset(&cfg, 0, sizeof(cfg));
237 g_assert(!throttle_enabled(&cfg));
238
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));
243 }
244
245 for (i = 0; i < BUCKETS_COUNT; i++) {
246 memset(&cfg, 0, sizeof(cfg));
247 set_cfg_value(false, i, -150);
248 g_assert(!throttle_enabled(&cfg));
249 }
250}
251
252/* tests functions for throttle_conflicting */
253
254static void test_conflicts_for_one_set(bool is_max,
255 int total,
256 int read,
257 int write)
258{
259 memset(&cfg, 0, sizeof(cfg));
6921b180 260 g_assert(!throttle_conflicting(&cfg, NULL));
f17cfe81
BC
261
262 set_cfg_value(is_max, total, 1);
263 set_cfg_value(is_max, read, 1);
6921b180 264 g_assert(throttle_conflicting(&cfg, NULL));
f17cfe81
BC
265
266 memset(&cfg, 0, sizeof(cfg));
267 set_cfg_value(is_max, total, 1);
268 set_cfg_value(is_max, write, 1);
6921b180 269 g_assert(throttle_conflicting(&cfg, NULL));
f17cfe81
BC
270
271 memset(&cfg, 0, sizeof(cfg));
272 set_cfg_value(is_max, total, 1);
273 set_cfg_value(is_max, read, 1);
274 set_cfg_value(is_max, write, 1);
6921b180 275 g_assert(throttle_conflicting(&cfg, NULL));
f17cfe81
BC
276
277 memset(&cfg, 0, sizeof(cfg));
278 set_cfg_value(is_max, total, 1);
6921b180 279 g_assert(!throttle_conflicting(&cfg, NULL));
f17cfe81
BC
280
281 memset(&cfg, 0, sizeof(cfg));
282 set_cfg_value(is_max, read, 1);
283 set_cfg_value(is_max, write, 1);
6921b180 284 g_assert(!throttle_conflicting(&cfg, NULL));
f17cfe81
BC
285}
286
287static void test_conflicting_config(void)
288{
289 /* bps average conflicts */
290 test_conflicts_for_one_set(false,
291 THROTTLE_BPS_TOTAL,
292 THROTTLE_BPS_READ,
293 THROTTLE_BPS_WRITE);
294
295 /* ops average conflicts */
296 test_conflicts_for_one_set(false,
297 THROTTLE_OPS_TOTAL,
298 THROTTLE_OPS_READ,
299 THROTTLE_OPS_WRITE);
300
301 /* bps average conflicts */
302 test_conflicts_for_one_set(true,
303 THROTTLE_BPS_TOTAL,
304 THROTTLE_BPS_READ,
305 THROTTLE_BPS_WRITE);
306 /* ops average conflicts */
307 test_conflicts_for_one_set(true,
308 THROTTLE_OPS_TOTAL,
309 THROTTLE_OPS_READ,
310 THROTTLE_OPS_WRITE);
311}
312/* functions to test the throttle_is_valid function */
313static void test_is_valid_for_value(int value, bool should_be_valid)
314{
315 int is_max, index;
316 for (is_max = 0; is_max < 2; is_max++) {
317 for (index = 0; index < BUCKETS_COUNT; index++) {
318 memset(&cfg, 0, sizeof(cfg));
319 set_cfg_value(is_max, index, value);
03ba36c8 320 g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid);
f17cfe81
BC
321 }
322 }
323}
324
325static void test_is_valid(void)
326{
327 /* negative number are invalid */
328 test_is_valid_for_value(-1, false);
329 /* zero are valids */
330 test_is_valid_for_value(0, true);
331 /* positives numers are valids */
332 test_is_valid_for_value(1, true);
333}
334
92e11a17
SH
335static void test_max_is_missing_limit(void)
336{
337 int i;
338
339 for (i = 0; i < BUCKETS_COUNT; i++) {
340 memset(&cfg, 0, sizeof(cfg));
341 cfg.buckets[i].max = 100;
342 cfg.buckets[i].avg = 0;
45b2d418 343 g_assert(throttle_max_is_missing_limit(&cfg, NULL));
92e11a17
SH
344
345 cfg.buckets[i].max = 0;
346 cfg.buckets[i].avg = 0;
45b2d418 347 g_assert(!throttle_max_is_missing_limit(&cfg, NULL));
92e11a17
SH
348
349 cfg.buckets[i].max = 0;
350 cfg.buckets[i].avg = 100;
45b2d418 351 g_assert(!throttle_max_is_missing_limit(&cfg, NULL));
92e11a17
SH
352 }
353}
354
f17cfe81
BC
355static void test_have_timer(void)
356{
0e5b0a2d 357 /* zero structures */
f17cfe81 358 memset(&ts, 0, sizeof(ts));
0e5b0a2d 359 memset(&tt, 0, sizeof(tt));
f17cfe81 360
73f395fa 361 /* no timer set should return false */
0e5b0a2d 362 g_assert(!throttle_timers_are_initialized(&tt));
f17cfe81 363
0e5b0a2d
BC
364 /* init structures */
365 throttle_init(&ts);
366 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
367 read_timer_cb, write_timer_cb, &ts);
f17cfe81
BC
368
369 /* timer set by init should return true */
0e5b0a2d 370 g_assert(throttle_timers_are_initialized(&tt));
f17cfe81 371
0e5b0a2d 372 throttle_timers_destroy(&tt);
f17cfe81
BC
373}
374
22524f72
SH
375static void test_detach_attach(void)
376{
0e5b0a2d 377 /* zero structures */
22524f72 378 memset(&ts, 0, sizeof(ts));
0e5b0a2d 379 memset(&tt, 0, sizeof(tt));
22524f72
SH
380
381 /* init the structure */
0e5b0a2d
BC
382 throttle_init(&ts);
383 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
384 read_timer_cb, write_timer_cb, &ts);
22524f72
SH
385
386 /* timer set by init should return true */
0e5b0a2d 387 g_assert(throttle_timers_are_initialized(&tt));
22524f72
SH
388
389 /* timer should no longer exist after detaching */
0e5b0a2d
BC
390 throttle_timers_detach_aio_context(&tt);
391 g_assert(!throttle_timers_are_initialized(&tt));
22524f72
SH
392
393 /* timer should exist again after attaching */
0e5b0a2d
BC
394 throttle_timers_attach_aio_context(&tt, ctx);
395 g_assert(throttle_timers_are_initialized(&tt));
22524f72 396
0e5b0a2d 397 throttle_timers_destroy(&tt);
22524f72
SH
398}
399
f17cfe81
BC
400static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
401 int size, /* size of the operation to do */
402 double avg, /* io limit */
403 uint64_t op_size, /* ideal size of an io */
404 double total_result,
405 double read_result,
406 double write_result)
407{
408 BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
409 THROTTLE_BPS_READ,
410 THROTTLE_BPS_WRITE, },
411 { THROTTLE_OPS_TOTAL,
412 THROTTLE_OPS_READ,
413 THROTTLE_OPS_WRITE, } };
414 ThrottleConfig cfg;
415 BucketType index;
416 int i;
417
418 for (i = 0; i < 3; i++) {
419 BucketType index = to_test[is_ops][i];
420 cfg.buckets[index].avg = avg;
421 }
422
423 cfg.op_size = op_size;
424
0e5b0a2d
BC
425 throttle_init(&ts);
426 throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
427 read_timer_cb, write_timer_cb, &ts);
428 throttle_config(&ts, &tt, &cfg);
f17cfe81
BC
429
430 /* account a read */
431 throttle_account(&ts, false, size);
432 /* account a write */
433 throttle_account(&ts, true, size);
434
435 /* check total result */
436 index = to_test[is_ops][0];
437 if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
438 return false;
439 }
440
441 /* check read result */
442 index = to_test[is_ops][1];
443 if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
444 return false;
445 }
446
447 /* check write result */
448 index = to_test[is_ops][2];
449 if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
450 return false;
451 }
452
0e5b0a2d 453 throttle_timers_destroy(&tt);
f17cfe81
BC
454
455 return true;
456}
457
458static void test_accounting(void)
459{
460 /* tests for bps */
461
462 /* op of size 1 */
463 g_assert(do_test_accounting(false,
464 1 * 512,
465 150,
466 0,
467 1024,
468 512,
469 512));
470
471 /* op of size 2 */
472 g_assert(do_test_accounting(false,
473 2 * 512,
474 150,
475 0,
476 2048,
477 1024,
478 1024));
479
480 /* op of size 2 and orthogonal parameter change */
481 g_assert(do_test_accounting(false,
482 2 * 512,
483 150,
484 17,
485 2048,
486 1024,
487 1024));
488
489
490 /* tests for ops */
491
492 /* op of size 1 */
493 g_assert(do_test_accounting(true,
494 1 * 512,
495 150,
496 0,
497 2,
498 1,
499 1));
500
501 /* op of size 2 */
502 g_assert(do_test_accounting(true,
503 2 * 512,
504 150,
505 0,
506 2,
507 1,
508 1));
509
510 /* jumbo op accounting fragmentation : size 64 with op size of 13 units */
511 g_assert(do_test_accounting(true,
512 64 * 512,
513 150,
514 13 * 512,
515 (64.0 * 2) / 13,
516 (64.0 / 13),
517 (64.0 / 13)));
518
519 /* same with orthogonal parameters changes */
520 g_assert(do_test_accounting(true,
521 64 * 512,
522 300,
523 13 * 512,
524 (64.0 * 2) / 13,
525 (64.0 / 13),
526 (64.0 / 13)));
527}
528
1fee955f
AG
529static void test_groups(void)
530{
531 ThrottleConfig cfg1, cfg2;
532 BlockDriverState *bdrv1, *bdrv2, *bdrv3;
533
534 bdrv1 = bdrv_new();
535 bdrv2 = bdrv_new();
536 bdrv3 = bdrv_new();
537
538 g_assert(bdrv1->throttle_state == NULL);
539 g_assert(bdrv2->throttle_state == NULL);
540 g_assert(bdrv3->throttle_state == NULL);
541
542 throttle_group_register_bs(bdrv1, "bar");
543 throttle_group_register_bs(bdrv2, "foo");
544 throttle_group_register_bs(bdrv3, "bar");
545
546 g_assert(bdrv1->throttle_state != NULL);
547 g_assert(bdrv2->throttle_state != NULL);
548 g_assert(bdrv3->throttle_state != NULL);
549
550 g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar"));
551 g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo"));
552 g_assert(bdrv1->throttle_state == bdrv3->throttle_state);
553
554 /* Setting the config of a group member affects the whole group */
555 memset(&cfg1, 0, sizeof(cfg1));
556 cfg1.buckets[THROTTLE_BPS_READ].avg = 500000;
557 cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
558 cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
559 cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
560 throttle_group_config(bdrv1, &cfg1);
561
562 throttle_group_get_config(bdrv1, &cfg1);
563 throttle_group_get_config(bdrv3, &cfg2);
564 g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
565
566 cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
567 cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
568 cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
569 cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
570 throttle_group_config(bdrv3, &cfg1);
571
572 throttle_group_get_config(bdrv1, &cfg1);
573 throttle_group_get_config(bdrv3, &cfg2);
574 g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
575
576 throttle_group_unregister_bs(bdrv1);
577 throttle_group_unregister_bs(bdrv2);
578 throttle_group_unregister_bs(bdrv3);
579
580 g_assert(bdrv1->throttle_state == NULL);
581 g_assert(bdrv2->throttle_state == NULL);
582 g_assert(bdrv3->throttle_state == NULL);
583}
584
f17cfe81
BC
585int main(int argc, char **argv)
586{
73eaa047 587 qemu_init_main_loop(&error_fatal);
1fee955f 588 ctx = qemu_get_aio_context();
1fee955f 589 bdrv_init();
13af91eb 590
f17cfe81
BC
591 do {} while (g_main_context_iteration(NULL, false));
592
593 /* tests in the same order as the header function declarations */
594 g_test_init(&argc, &argv, NULL);
595 g_test_add_func("/throttle/leak_bucket", test_leak_bucket);
596 g_test_add_func("/throttle/compute_wait", test_compute_wait);
597 g_test_add_func("/throttle/init", test_init);
598 g_test_add_func("/throttle/destroy", test_destroy);
599 g_test_add_func("/throttle/have_timer", test_have_timer);
22524f72 600 g_test_add_func("/throttle/detach_attach", test_detach_attach);
f17cfe81
BC
601 g_test_add_func("/throttle/config/enabled", test_enabled);
602 g_test_add_func("/throttle/config/conflicting", test_conflicting_config);
603 g_test_add_func("/throttle/config/is_valid", test_is_valid);
92e11a17 604 g_test_add_func("/throttle/config/max", test_max_is_missing_limit);
f17cfe81
BC
605 g_test_add_func("/throttle/config_functions", test_config_functions);
606 g_test_add_func("/throttle/accounting", test_accounting);
1fee955f 607 g_test_add_func("/throttle/groups", test_groups);
f17cfe81
BC
608 return g_test_run();
609}
610