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