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