]> Git Repo - qemu.git/blob - tests/test-throttle.c
throttle: Extract timers from ThrottleState into a separate structure
[qemu.git] / tests / test-throttle.c
1 /*
2  * Throttle infrastructure tests
3  *
4  * Copyright Nodalink, SARL. 2013
5  *
6  * Authors:
7  *  BenoĆ®t Canet     <[email protected]>
8  *
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.
11  */
12
13 #include <glib.h>
14 #include <math.h>
15 #include "block/aio.h"
16 #include "qemu/throttle.h"
17 #include "qemu/error-report.h"
18
19 static AioContext     *ctx;
20 static LeakyBucket    bkt;
21 static ThrottleConfig cfg;
22 static ThrottleState  ts;
23 static ThrottleTimers tt;
24
25 /* useful function */
26 static bool double_cmp(double x, double y)
27 {
28     return fabsl(x - y) < 1e-6;
29 }
30
31 /* tests for single bucket operations */
32 static void test_leak_bucket(void)
33 {
34     /* set initial value */
35     bkt.avg = 150;
36     bkt.max = 15;
37     bkt.level = 1.5;
38
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));
44
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));
50
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));
56 }
57
58 static void test_compute_wait(void)
59 {
60     int64_t wait;
61     int64_t result;
62
63     /* no operation limit set */
64     bkt.avg = 0;
65     bkt.max = 15;
66     bkt.level = 1.5;
67     wait = throttle_compute_wait(&bkt);
68     g_assert(!wait);
69
70     /* zero delta */
71     bkt.avg = 150;
72     bkt.max = 15;
73     bkt.level = 15;
74     wait = throttle_compute_wait(&bkt);
75     g_assert(!wait);
76
77     /* below zero delta */
78     bkt.avg = 150;
79     bkt.max = 15;
80     bkt.level = 9;
81     wait = throttle_compute_wait(&bkt);
82     g_assert(!wait);
83
84     /* half an operation above max */
85     bkt.avg = 150;
86     bkt.max = 15;
87     bkt.level = 15.5;
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);
92 }
93
94 /* functions to test ThrottleState initialization/destroy methods */
95 static void read_timer_cb(void *opaque)
96 {
97 }
98
99 static void write_timer_cb(void *opaque)
100 {
101 }
102
103 static void test_init(void)
104 {
105     int i;
106
107     /* fill the structures with crap */
108     memset(&ts, 1, sizeof(ts));
109     memset(&tt, 1, sizeof(tt));
110
111     /* init structures */
112     throttle_init(&ts);
113     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
114                          read_timer_cb, write_timer_cb, &ts);
115
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]);
120
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);
128     }
129
130     throttle_timers_destroy(&tt);
131 }
132
133 static void test_destroy(void)
134 {
135     int i;
136     throttle_init(&ts);
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]);
142     }
143 }
144
145 /* function to test throttle_config and throttle_get_config */
146 static void test_config_functions(void)
147 {
148     int i;
149     ThrottleConfig orig_cfg, final_cfg;
150
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;
154
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;
158
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;
162
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;
166
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;
170
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;
174
175     orig_cfg.op_size = 1;
176
177     throttle_init(&ts);
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);
183
184     /* has previous leak been initialized by throttle_config ? */
185     g_assert(ts.previous_leak);
186
187     /* get back the fixed configuration */
188     throttle_get_config(&ts, &final_cfg);
189
190     throttle_timers_destroy(&tt);
191
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);
195
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);
199
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);
203
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);
207
208     g_assert(final_cfg.op_size == 1);
209
210     /* check bucket have been cleared */
211     for (i = 0; i < BUCKETS_COUNT; i++) {
212         g_assert(!final_cfg.buckets[i].level);
213     }
214 }
215
216 /* functions to test is throttle is enabled by a config */
217 static void set_cfg_value(bool is_max, int index, int value)
218 {
219     if (is_max) {
220         cfg.buckets[index].max = value;
221     } else {
222         cfg.buckets[index].avg = value;
223     }
224 }
225
226 static void test_enabled(void)
227 {
228     int i;
229
230     memset(&cfg, 0, sizeof(cfg));
231     g_assert(!throttle_enabled(&cfg));
232
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));
237     }
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
246 /* tests functions for throttle_conflicting */
247
248 static void test_conflicts_for_one_set(bool is_max,
249                                        int total,
250                                        int read,
251                                        int write)
252 {
253     memset(&cfg, 0, sizeof(cfg));
254     g_assert(!throttle_conflicting(&cfg));
255
256     set_cfg_value(is_max, total, 1);
257     set_cfg_value(is_max, read,  1);
258     g_assert(throttle_conflicting(&cfg));
259
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));
264
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));
270
271     memset(&cfg, 0, sizeof(cfg));
272     set_cfg_value(is_max, total, 1);
273     g_assert(!throttle_conflicting(&cfg));
274
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));
279 }
280
281 static void test_conflicting_config(void)
282 {
283     /* bps average conflicts */
284     test_conflicts_for_one_set(false,
285                                THROTTLE_BPS_TOTAL,
286                                THROTTLE_BPS_READ,
287                                THROTTLE_BPS_WRITE);
288
289     /* ops average conflicts */
290     test_conflicts_for_one_set(false,
291                                THROTTLE_OPS_TOTAL,
292                                THROTTLE_OPS_READ,
293                                THROTTLE_OPS_WRITE);
294
295     /* bps average conflicts */
296     test_conflicts_for_one_set(true,
297                                THROTTLE_BPS_TOTAL,
298                                THROTTLE_BPS_READ,
299                                THROTTLE_BPS_WRITE);
300     /* ops average conflicts */
301     test_conflicts_for_one_set(true,
302                                THROTTLE_OPS_TOTAL,
303                                THROTTLE_OPS_READ,
304                                THROTTLE_OPS_WRITE);
305 }
306 /* functions to test the throttle_is_valid function */
307 static void test_is_valid_for_value(int value, bool should_be_valid)
308 {
309     int is_max, index;
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);
315         }
316     }
317 }
318
319 static void test_is_valid(void)
320 {
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);
327 }
328
329 static void test_have_timer(void)
330 {
331     /* zero structures */
332     memset(&ts, 0, sizeof(ts));
333     memset(&tt, 0, sizeof(tt));
334
335     /* no timer set should return false */
336     g_assert(!throttle_timers_are_initialized(&tt));
337
338     /* init structures */
339     throttle_init(&ts);
340     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
341                          read_timer_cb, write_timer_cb, &ts);
342
343     /* timer set by init should return true */
344     g_assert(throttle_timers_are_initialized(&tt));
345
346     throttle_timers_destroy(&tt);
347 }
348
349 static void test_detach_attach(void)
350 {
351     /* zero structures */
352     memset(&ts, 0, sizeof(ts));
353     memset(&tt, 0, sizeof(tt));
354
355     /* init the structure */
356     throttle_init(&ts);
357     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
358                          read_timer_cb, write_timer_cb, &ts);
359
360     /* timer set by init should return true */
361     g_assert(throttle_timers_are_initialized(&tt));
362
363     /* timer should no longer exist after detaching */
364     throttle_timers_detach_aio_context(&tt);
365     g_assert(!throttle_timers_are_initialized(&tt));
366
367     /* timer should exist again after attaching */
368     throttle_timers_attach_aio_context(&tt, ctx);
369     g_assert(throttle_timers_are_initialized(&tt));
370
371     throttle_timers_destroy(&tt);
372 }
373
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 */
378                 double total_result,
379                 double read_result,
380                 double write_result)
381 {
382     BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
383                                    THROTTLE_BPS_READ,
384                                    THROTTLE_BPS_WRITE, },
385                                  { THROTTLE_OPS_TOTAL,
386                                    THROTTLE_OPS_READ,
387                                    THROTTLE_OPS_WRITE, } };
388     ThrottleConfig cfg;
389     BucketType index;
390     int i;
391
392     for (i = 0; i < 3; i++) {
393         BucketType index = to_test[is_ops][i];
394         cfg.buckets[index].avg = avg;
395     }
396
397     cfg.op_size = op_size;
398
399     throttle_init(&ts);
400     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
401                          read_timer_cb, write_timer_cb, &ts);
402     throttle_config(&ts, &tt, &cfg);
403
404     /* account a read */
405     throttle_account(&ts, false, size);
406     /* account a write */
407     throttle_account(&ts, true, size);
408
409     /* check total result */
410     index = to_test[is_ops][0];
411     if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
412         return false;
413     }
414
415     /* check read result */
416     index = to_test[is_ops][1];
417     if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
418         return false;
419     }
420
421     /* check write result */
422     index = to_test[is_ops][2];
423     if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
424         return false;
425     }
426
427     throttle_timers_destroy(&tt);
428
429     return true;
430 }
431
432 static void test_accounting(void)
433 {
434     /* tests for bps */
435
436     /* op of size 1 */
437     g_assert(do_test_accounting(false,
438                                 1 * 512,
439                                 150,
440                                 0,
441                                 1024,
442                                 512,
443                                 512));
444
445     /* op of size 2 */
446     g_assert(do_test_accounting(false,
447                                 2 * 512,
448                                 150,
449                                 0,
450                                 2048,
451                                 1024,
452                                 1024));
453
454     /* op of size 2 and orthogonal parameter change */
455     g_assert(do_test_accounting(false,
456                                 2 * 512,
457                                 150,
458                                 17,
459                                 2048,
460                                 1024,
461                                 1024));
462
463
464     /* tests for ops */
465
466     /* op of size 1 */
467     g_assert(do_test_accounting(true,
468                                 1 * 512,
469                                 150,
470                                 0,
471                                 2,
472                                 1,
473                                 1));
474
475     /* op of size 2 */
476     g_assert(do_test_accounting(true,
477                                 2 *  512,
478                                 150,
479                                 0,
480                                 2,
481                                 1,
482                                 1));
483
484     /* jumbo op accounting fragmentation : size 64 with op size of 13 units */
485     g_assert(do_test_accounting(true,
486                                 64 * 512,
487                                 150,
488                                 13 * 512,
489                                 (64.0 * 2) / 13,
490                                 (64.0 / 13),
491                                 (64.0 / 13)));
492
493     /* same with orthogonal parameters changes */
494     g_assert(do_test_accounting(true,
495                                 64 * 512,
496                                 300,
497                                 13 * 512,
498                                 (64.0 * 2) / 13,
499                                 (64.0 / 13),
500                                 (64.0 / 13)));
501 }
502
503 int main(int argc, char **argv)
504 {
505     GSource *src;
506     Error *local_error = NULL;
507
508     init_clocks();
509
510     ctx = aio_context_new(&local_error);
511     if (!ctx) {
512         error_report("Failed to create AIO Context: '%s'",
513                      error_get_pretty(local_error));
514         error_free(local_error);
515         exit(1);
516     }
517     src = aio_get_g_source(ctx);
518     g_source_attach(src, NULL);
519     g_source_unref(src);
520
521     do {} while (g_main_context_iteration(NULL, false));
522
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);
536     return g_test_run();
537 }
538
This page took 0.053639 seconds and 4 git commands to generate.