qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

test-replication.c (16668B)


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