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