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" 26static char *p_local_disk; 27 28/* secondary */ 29#define S_ID "secondary-id" 30#define S_LOCAL_DISK_ID "secondary-local-disk-id" 31static char *s_local_disk; 32static char *s_active_disk; 33static char *s_hidden_disk; 34 35/* FIXME: steal from blockdev.c */ 36QemuOptsList 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 46static void blk_rw_done(void *opaque, int ret) 47{ 48 *(int *)opaque = ret; 49} 50 51static 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 96static 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 */ 131static void make_temp(char *template) 132{ 133 int fd; 134 135 fd = mkstemp(template); 136 g_assert(fd >= 0); 137 close(fd); 138} 139 140static 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 160static 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 171static 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 199static 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 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 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 256static 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 269static 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 280static 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 291static 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 344static 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 371static 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 383static 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 396static 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 429static 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 463static 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 507static 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 536static 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 552static void sigabrt_handler(int signo) 553{ 554 cleanup_imgs(); 555} 556 557static 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 573int 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}