qemu/tests/test-replication.c
<<
>>
Prefs
   1/*
   2 * Block replication tests
   3 *
   4 * Copyright (c) 2016 FUJITSU LIMITED
   5 * Author: Changlong Xie <xiecl.fnst@cn.fujitsu.com>
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or
   8 * later.  See the COPYING file in the top-level directory.
   9 */
  10
  11#include "qemu/osdep.h"
  12
  13#include "qapi/error.h"
  14#include "qapi/qmp/qdict.h"
  15#include "qemu/option.h"
  16#include "replication.h"
  17#include "block/block_int.h"
  18#include "block/qdict.h"
  19#include "sysemu/block-backend.h"
  20
  21#define IMG_SIZE (64 * 1024 * 1024)
  22
  23/* primary */
  24#define P_ID "primary-id"
  25static char p_local_disk[] = "/tmp/p_local_disk.XXXXXX";
  26
  27/* secondary */
  28#define S_ID "secondary-id"
  29#define S_LOCAL_DISK_ID "secondary-local-disk-id"
  30static char s_local_disk[] = "/tmp/s_local_disk.XXXXXX";
  31static char s_active_disk[] = "/tmp/s_active_disk.XXXXXX";
  32static char s_hidden_disk[] = "/tmp/s_hidden_disk.XXXXXX";
  33
  34/* FIXME: steal from blockdev.c */
  35QemuOptsList qemu_drive_opts = {
  36    .name = "drive",
  37    .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
  38    .desc = {
  39        { /* end of list */ }
  40    },
  41};
  42
  43#define NOT_DONE 0x7fffffff
  44
  45static void blk_rw_done(void *opaque, int ret)
  46{
  47    *(int *)opaque = ret;
  48}
  49
  50static void test_blk_read(BlockBackend *blk, long pattern,
  51                          int64_t pattern_offset, int64_t pattern_count,
  52                          int64_t offset, int64_t count,
  53                          bool expect_failed)
  54{
  55    void *pattern_buf = NULL;
  56    QEMUIOVector qiov;
  57    void *cmp_buf = NULL;
  58    int async_ret = NOT_DONE;
  59
  60    if (pattern) {
  61        cmp_buf = g_malloc(pattern_count);
  62        memset(cmp_buf, pattern, pattern_count);
  63    }
  64
  65    pattern_buf = g_malloc(count);
  66    if (pattern) {
  67        memset(pattern_buf, pattern, count);
  68    } else {
  69        memset(pattern_buf, 0x00, count);
  70    }
  71
  72    qemu_iovec_init(&qiov, 1);
  73    qemu_iovec_add(&qiov, pattern_buf, count);
  74
  75    blk_aio_preadv(blk, offset, &qiov, 0, blk_rw_done, &async_ret);
  76    while (async_ret == NOT_DONE) {
  77        main_loop_wait(false);
  78    }
  79
  80    if (expect_failed) {
  81        g_assert(async_ret != 0);
  82    } else {
  83        g_assert(async_ret == 0);
  84        if (pattern) {
  85            g_assert(memcmp(pattern_buf + pattern_offset,
  86                            cmp_buf, pattern_count) <= 0);
  87        }
  88    }
  89
  90    g_free(pattern_buf);
  91    g_free(cmp_buf);
  92    qemu_iovec_destroy(&qiov);
  93}
  94
  95static void test_blk_write(BlockBackend *blk, long pattern, int64_t offset,
  96                           int64_t count, bool expect_failed)
  97{
  98    void *pattern_buf = NULL;
  99    QEMUIOVector qiov;
 100    int async_ret = NOT_DONE;
 101
 102    pattern_buf = g_malloc(count);
 103    if (pattern) {
 104        memset(pattern_buf, pattern, count);
 105    } else {
 106        memset(pattern_buf, 0x00, count);
 107    }
 108
 109    qemu_iovec_init(&qiov, 1);
 110    qemu_iovec_add(&qiov, pattern_buf, count);
 111
 112    blk_aio_pwritev(blk, offset, &qiov, 0, blk_rw_done, &async_ret);
 113    while (async_ret == NOT_DONE) {
 114        main_loop_wait(false);
 115    }
 116
 117    if (expect_failed) {
 118        g_assert(async_ret != 0);
 119    } else {
 120        g_assert(async_ret == 0);
 121    }
 122
 123    g_free(pattern_buf);
 124    qemu_iovec_destroy(&qiov);
 125}
 126
 127/*
 128 * Create a uniquely-named empty temporary file.
 129 */
 130static void make_temp(char *template)
 131{
 132    int fd;
 133
 134    fd = mkstemp(template);
 135    g_assert(fd >= 0);
 136    close(fd);
 137}
 138
 139static void prepare_imgs(void)
 140{
 141    Error *local_err = NULL;
 142
 143    make_temp(p_local_disk);
 144    make_temp(s_local_disk);
 145    make_temp(s_active_disk);
 146    make_temp(s_hidden_disk);
 147
 148    /* Primary */
 149    bdrv_img_create(p_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE,
 150                    BDRV_O_RDWR, true, &local_err);
 151    g_assert(!local_err);
 152
 153    /* Secondary */
 154    bdrv_img_create(s_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE,
 155                    BDRV_O_RDWR, true, &local_err);
 156    g_assert(!local_err);
 157    bdrv_img_create(s_active_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE,
 158                    BDRV_O_RDWR, true, &local_err);
 159    g_assert(!local_err);
 160    bdrv_img_create(s_hidden_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE,
 161                    BDRV_O_RDWR, true, &local_err);
 162    g_assert(!local_err);
 163}
 164
 165static void cleanup_imgs(void)
 166{
 167    /* Primary */
 168    unlink(p_local_disk);
 169
 170    /* Secondary */
 171    unlink(s_local_disk);
 172    unlink(s_active_disk);
 173    unlink(s_hidden_disk);
 174}
 175
 176static BlockBackend *start_primary(void)
 177{
 178    BlockBackend *blk;
 179    QemuOpts *opts;
 180    QDict *qdict;
 181    Error *local_err = NULL;
 182    char *cmdline;
 183
 184    cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx,"
 185                              "file.driver=qcow2,file.file.filename=%s,"
 186                              "file.file.locking=off"
 187                              , p_local_disk);
 188    opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
 189    g_free(cmdline);
 190
 191    qdict = qemu_opts_to_qdict(opts, NULL);
 192    qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
 193    qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 194
 195    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
 196    g_assert(blk);
 197    g_assert(!local_err);
 198
 199    monitor_add_blk(blk, P_ID, &local_err);
 200    g_assert(!local_err);
 201
 202    qemu_opts_del(opts);
 203
 204    return blk;
 205}
 206
 207static void teardown_primary(void)
 208{
 209    BlockBackend *blk;
 210    AioContext *ctx;
 211
 212    /* remove P_ID */
 213    blk = blk_by_name(P_ID);
 214    assert(blk);
 215
 216    ctx = blk_get_aio_context(blk);
 217    aio_context_acquire(ctx);
 218    monitor_remove_blk(blk);
 219    blk_unref(blk);
 220    aio_context_release(ctx);
 221}
 222
 223static void test_primary_read(void)
 224{
 225    BlockBackend *blk;
 226
 227    blk = start_primary();
 228
 229    /* read from 0 to IMG_SIZE */
 230    test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true);
 231
 232    teardown_primary();
 233}
 234
 235static void test_primary_write(void)
 236{
 237    BlockBackend *blk;
 238
 239    blk = start_primary();
 240
 241    /* write from 0 to IMG_SIZE */
 242    test_blk_write(blk, 0, 0, IMG_SIZE, true);
 243
 244    teardown_primary();
 245}
 246
 247static void test_primary_start(void)
 248{
 249    BlockBackend *blk = NULL;
 250    Error *local_err = NULL;
 251
 252    blk = start_primary();
 253
 254    replication_start_all(REPLICATION_MODE_PRIMARY, &local_err);
 255    g_assert(!local_err);
 256
 257    /* read from 0 to IMG_SIZE */
 258    test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true);
 259
 260    /* write 0x22 from 0 to IMG_SIZE */
 261    test_blk_write(blk, 0x22, 0, IMG_SIZE, false);
 262
 263    teardown_primary();
 264}
 265
 266static void test_primary_stop(void)
 267{
 268    Error *local_err = NULL;
 269    bool failover = true;
 270
 271    start_primary();
 272
 273    replication_start_all(REPLICATION_MODE_PRIMARY, &local_err);
 274    g_assert(!local_err);
 275
 276    replication_stop_all(failover, &local_err);
 277    g_assert(!local_err);
 278
 279    teardown_primary();
 280}
 281
 282static void test_primary_do_checkpoint(void)
 283{
 284    Error *local_err = NULL;
 285
 286    start_primary();
 287
 288    replication_start_all(REPLICATION_MODE_PRIMARY, &local_err);
 289    g_assert(!local_err);
 290
 291    replication_do_checkpoint_all(&local_err);
 292    g_assert(!local_err);
 293
 294    teardown_primary();
 295}
 296
 297static void test_primary_get_error_all(void)
 298{
 299    Error *local_err = NULL;
 300
 301    start_primary();
 302
 303    replication_start_all(REPLICATION_MODE_PRIMARY, &local_err);
 304    g_assert(!local_err);
 305
 306    replication_get_error_all(&local_err);
 307    g_assert(!local_err);
 308
 309    teardown_primary();
 310}
 311
 312static BlockBackend *start_secondary(void)
 313{
 314    QemuOpts *opts;
 315    QDict *qdict;
 316    BlockBackend *blk;
 317    char *cmdline;
 318    Error *local_err = NULL;
 319
 320    /* add s_local_disk and forge S_LOCAL_DISK_ID */
 321    cmdline = g_strdup_printf("file.filename=%s,driver=qcow2,"
 322                              "file.locking=off",
 323                              s_local_disk);
 324    opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
 325    g_free(cmdline);
 326
 327    qdict = qemu_opts_to_qdict(opts, NULL);
 328    qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
 329    qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 330
 331    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
 332    assert(blk);
 333    monitor_add_blk(blk, S_LOCAL_DISK_ID, &local_err);
 334    g_assert(!local_err);
 335
 336    /* format s_local_disk with pattern "0x11" */
 337    test_blk_write(blk, 0x11, 0, IMG_SIZE, false);
 338
 339    qemu_opts_del(opts);
 340
 341    /* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */
 342    cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s,"
 343                              "file.driver=qcow2,file.file.filename=%s,"
 344                              "file.file.locking=off,"
 345                              "file.backing.driver=qcow2,"
 346                              "file.backing.file.filename=%s,"
 347                              "file.backing.file.locking=off,"
 348                              "file.backing.backing=%s"
 349                              , S_ID, s_active_disk, s_hidden_disk
 350                              , S_LOCAL_DISK_ID);
 351    opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
 352    g_free(cmdline);
 353
 354    qdict = qemu_opts_to_qdict(opts, NULL);
 355    qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
 356    qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 357
 358    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
 359    assert(blk);
 360    monitor_add_blk(blk, S_ID, &local_err);
 361    g_assert(!local_err);
 362
 363    qemu_opts_del(opts);
 364
 365    return blk;
 366}
 367
 368static void teardown_secondary(void)
 369{
 370    /* only need to destroy two BBs */
 371    BlockBackend *blk;
 372    AioContext *ctx;
 373
 374    /* remove S_LOCAL_DISK_ID */
 375    blk = blk_by_name(S_LOCAL_DISK_ID);
 376    assert(blk);
 377
 378    ctx = blk_get_aio_context(blk);
 379    aio_context_acquire(ctx);
 380    monitor_remove_blk(blk);
 381    blk_unref(blk);
 382    aio_context_release(ctx);
 383
 384    /* remove S_ID */
 385    blk = blk_by_name(S_ID);
 386    assert(blk);
 387
 388    ctx = blk_get_aio_context(blk);
 389    aio_context_acquire(ctx);
 390    monitor_remove_blk(blk);
 391    blk_unref(blk);
 392    aio_context_release(ctx);
 393}
 394
 395static void test_secondary_read(void)
 396{
 397    BlockBackend *blk;
 398
 399    blk = start_secondary();
 400
 401    /* read from 0 to IMG_SIZE */
 402    test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true);
 403
 404    teardown_secondary();
 405}
 406
 407static void test_secondary_write(void)
 408{
 409    BlockBackend *blk;
 410
 411    blk = start_secondary();
 412
 413    /* write from 0 to IMG_SIZE */
 414    test_blk_write(blk, 0, 0, IMG_SIZE, true);
 415
 416    teardown_secondary();
 417}
 418
 419static void test_secondary_start(void)
 420{
 421    BlockBackend *top_blk, *local_blk;
 422    Error *local_err = NULL;
 423    bool failover = true;
 424
 425    top_blk = start_secondary();
 426    replication_start_all(REPLICATION_MODE_SECONDARY, &local_err);
 427    g_assert(!local_err);
 428
 429    /* read from s_local_disk (0, IMG_SIZE) */
 430    test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false);
 431
 432    /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
 433    local_blk = blk_by_name(S_LOCAL_DISK_ID);
 434    test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false);
 435
 436    /* replication will backup s_local_disk to s_hidden_disk */
 437    test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
 438                  IMG_SIZE / 2, 0, IMG_SIZE, false);
 439
 440    /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */
 441    test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false);
 442
 443    /* read from s_active_disk (0, IMG_SIZE/2) */
 444    test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2,
 445                  0, IMG_SIZE / 2, false);
 446
 447    /* unblock top_bs */
 448    replication_stop_all(failover, &local_err);
 449    g_assert(!local_err);
 450
 451    teardown_secondary();
 452}
 453
 454
 455static void test_secondary_stop(void)
 456{
 457    BlockBackend *top_blk, *local_blk;
 458    Error *local_err = NULL;
 459    bool failover = true;
 460
 461    top_blk = start_secondary();
 462    replication_start_all(REPLICATION_MODE_SECONDARY, &local_err);
 463    g_assert(!local_err);
 464
 465    /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
 466    local_blk = blk_by_name(S_LOCAL_DISK_ID);
 467    test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false);
 468
 469    /* replication will backup s_local_disk to s_hidden_disk */
 470    test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
 471                  IMG_SIZE / 2, 0, IMG_SIZE, false);
 472
 473    /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */
 474    test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false);
 475
 476    /* do active commit */
 477    replication_stop_all(failover, &local_err);
 478    g_assert(!local_err);
 479
 480    /* read from s_local_disk (0, IMG_SIZE / 2) */
 481    test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2,
 482                  0, IMG_SIZE / 2, false);
 483
 484
 485    /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
 486    test_blk_read(top_blk, 0x22, IMG_SIZE / 2,
 487                  IMG_SIZE / 2, 0, IMG_SIZE, false);
 488
 489    teardown_secondary();
 490}
 491
 492static void test_secondary_do_checkpoint(void)
 493{
 494    BlockBackend *top_blk, *local_blk;
 495    Error *local_err = NULL;
 496    bool failover = true;
 497
 498    top_blk = start_secondary();
 499    replication_start_all(REPLICATION_MODE_SECONDARY, &local_err);
 500    g_assert(!local_err);
 501
 502    /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
 503    local_blk = blk_by_name(S_LOCAL_DISK_ID);
 504    test_blk_write(local_blk, 0x22, IMG_SIZE / 2,
 505                   IMG_SIZE / 2, false);
 506
 507    /* replication will backup s_local_disk to s_hidden_disk */
 508    test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
 509                  IMG_SIZE / 2, 0, IMG_SIZE, false);
 510
 511    replication_do_checkpoint_all(&local_err);
 512    g_assert(!local_err);
 513
 514    /* after checkpoint, read pattern 0x22 from s_local_disk */
 515    test_blk_read(top_blk, 0x22, IMG_SIZE / 2,
 516                  IMG_SIZE / 2, 0, IMG_SIZE, false);
 517
 518    /* unblock top_bs */
 519    replication_stop_all(failover, &local_err);
 520    g_assert(!local_err);
 521
 522    teardown_secondary();
 523}
 524
 525static void test_secondary_get_error_all(void)
 526{
 527    Error *local_err = NULL;
 528    bool failover = true;
 529
 530    start_secondary();
 531    replication_start_all(REPLICATION_MODE_SECONDARY, &local_err);
 532    g_assert(!local_err);
 533
 534    replication_get_error_all(&local_err);
 535    g_assert(!local_err);
 536
 537    /* unblock top_bs */
 538    replication_stop_all(failover, &local_err);
 539    g_assert(!local_err);
 540
 541    teardown_secondary();
 542}
 543
 544static void sigabrt_handler(int signo)
 545{
 546    cleanup_imgs();
 547}
 548
 549static void setup_sigabrt_handler(void)
 550{
 551    struct sigaction sigact;
 552
 553    sigact = (struct sigaction) {
 554        .sa_handler = sigabrt_handler,
 555        .sa_flags = SA_RESETHAND,
 556    };
 557    sigemptyset(&sigact.sa_mask);
 558    sigaction(SIGABRT, &sigact, NULL);
 559}
 560
 561int main(int argc, char **argv)
 562{
 563    int ret;
 564    qemu_init_main_loop(&error_fatal);
 565    bdrv_init();
 566
 567    g_test_init(&argc, &argv, NULL);
 568    setup_sigabrt_handler();
 569
 570    prepare_imgs();
 571
 572    /* Primary */
 573    g_test_add_func("/replication/primary/read",    test_primary_read);
 574    g_test_add_func("/replication/primary/write",   test_primary_write);
 575    g_test_add_func("/replication/primary/start",   test_primary_start);
 576    g_test_add_func("/replication/primary/stop",    test_primary_stop);
 577    g_test_add_func("/replication/primary/do_checkpoint",
 578                    test_primary_do_checkpoint);
 579    g_test_add_func("/replication/primary/get_error_all",
 580                    test_primary_get_error_all);
 581
 582    /* Secondary */
 583    g_test_add_func("/replication/secondary/read",  test_secondary_read);
 584    g_test_add_func("/replication/secondary/write", test_secondary_write);
 585    g_test_add_func("/replication/secondary/start", test_secondary_start);
 586    g_test_add_func("/replication/secondary/stop",  test_secondary_stop);
 587    g_test_add_func("/replication/secondary/do_checkpoint",
 588                    test_secondary_do_checkpoint);
 589    g_test_add_func("/replication/secondary/get_error_all",
 590                    test_secondary_get_error_all);
 591
 592    ret = g_test_run();
 593
 594    cleanup_imgs();
 595
 596    return ret;
 597}
 598