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 203 /* remove P_ID */ 204 blk = blk_by_name(P_ID); 205 assert(blk); 206 207 monitor_remove_blk(blk); 208 blk_unref(blk); 209 } 210 211 static void test_primary_read(void) 212 { 213 BlockBackend *blk; 214 215 blk = start_primary(); 216 217 /* read from 0 to IMG_SIZE */ 218 test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); 219 220 teardown_primary(); 221 } 222 223 static void test_primary_write(void) 224 { 225 BlockBackend *blk; 226 227 blk = start_primary(); 228 229 /* write from 0 to IMG_SIZE */ 230 test_blk_write(blk, 0, 0, IMG_SIZE, true); 231 232 teardown_primary(); 233 } 234 235 static void test_primary_start(void) 236 { 237 BlockBackend *blk = NULL; 238 239 blk = start_primary(); 240 241 replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); 242 243 /* read from 0 to IMG_SIZE */ 244 test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); 245 246 /* write 0x22 from 0 to IMG_SIZE */ 247 test_blk_write(blk, 0x22, 0, IMG_SIZE, false); 248 249 teardown_primary(); 250 } 251 252 static void test_primary_stop(void) 253 { 254 bool failover = true; 255 256 start_primary(); 257 258 replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); 259 260 replication_stop_all(failover, &error_abort); 261 262 teardown_primary(); 263 } 264 265 static void test_primary_do_checkpoint(void) 266 { 267 start_primary(); 268 269 replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); 270 271 replication_do_checkpoint_all(&error_abort); 272 273 teardown_primary(); 274 } 275 276 static void test_primary_get_error_all(void) 277 { 278 start_primary(); 279 280 replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); 281 282 replication_get_error_all(&error_abort); 283 284 teardown_primary(); 285 } 286 287 static BlockBackend *start_secondary(void) 288 { 289 QemuOpts *opts; 290 QDict *qdict; 291 BlockBackend *blk; 292 char *cmdline; 293 294 /* add s_local_disk and forge S_LOCAL_DISK_ID */ 295 cmdline = g_strdup_printf("file.filename=%s,driver=qcow2," 296 "file.locking=off", 297 s_local_disk); 298 opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); 299 g_free(cmdline); 300 301 qdict = qemu_opts_to_qdict(opts, NULL); 302 qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); 303 qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); 304 305 blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); 306 assert(blk); 307 monitor_add_blk(blk, S_LOCAL_DISK_ID, &error_abort); 308 309 /* format s_local_disk with pattern "0x11" */ 310 test_blk_write(blk, 0x11, 0, IMG_SIZE, false); 311 312 qemu_opts_del(opts); 313 314 /* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */ 315 cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s," 316 "file.driver=qcow2,file.file.filename=%s," 317 "file.file.locking=off," 318 "file.backing.driver=qcow2," 319 "file.backing.file.filename=%s," 320 "file.backing.file.locking=off," 321 "file.backing.backing=%s" 322 , S_ID, s_active_disk, s_hidden_disk 323 , S_LOCAL_DISK_ID); 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, &error_abort); 332 assert(blk); 333 monitor_add_blk(blk, S_ID, &error_abort); 334 335 qemu_opts_del(opts); 336 337 return blk; 338 } 339 340 static void teardown_secondary(void) 341 { 342 /* only need to destroy two BBs */ 343 BlockBackend *blk; 344 345 /* remove S_LOCAL_DISK_ID */ 346 blk = blk_by_name(S_LOCAL_DISK_ID); 347 assert(blk); 348 349 monitor_remove_blk(blk); 350 blk_unref(blk); 351 352 /* remove S_ID */ 353 blk = blk_by_name(S_ID); 354 assert(blk); 355 356 monitor_remove_blk(blk); 357 blk_unref(blk); 358 } 359 360 static void test_secondary_read(void) 361 { 362 BlockBackend *blk; 363 364 blk = start_secondary(); 365 366 /* read from 0 to IMG_SIZE */ 367 test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); 368 369 teardown_secondary(); 370 } 371 372 static void test_secondary_write(void) 373 { 374 BlockBackend *blk; 375 376 blk = start_secondary(); 377 378 /* write from 0 to IMG_SIZE */ 379 test_blk_write(blk, 0, 0, IMG_SIZE, true); 380 381 teardown_secondary(); 382 } 383 384 #ifndef _WIN32 385 static void test_secondary_start(void) 386 { 387 BlockBackend *top_blk, *local_blk; 388 bool failover = true; 389 390 top_blk = start_secondary(); 391 replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); 392 393 /* read from s_local_disk (0, IMG_SIZE) */ 394 test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false); 395 396 /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 397 local_blk = blk_by_name(S_LOCAL_DISK_ID); 398 test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); 399 400 /* replication will backup s_local_disk to s_hidden_disk */ 401 test_blk_read(top_blk, 0x11, IMG_SIZE / 2, 402 IMG_SIZE / 2, 0, IMG_SIZE, false); 403 404 /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ 405 test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); 406 407 /* read from s_active_disk (0, IMG_SIZE/2) */ 408 test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, 409 0, IMG_SIZE / 2, false); 410 411 /* unblock top_bs */ 412 replication_stop_all(failover, &error_abort); 413 414 teardown_secondary(); 415 } 416 417 418 static void test_secondary_stop(void) 419 { 420 BlockBackend *top_blk, *local_blk; 421 bool failover = true; 422 423 top_blk = start_secondary(); 424 replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); 425 426 /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 427 local_blk = blk_by_name(S_LOCAL_DISK_ID); 428 test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); 429 430 /* replication will backup s_local_disk to s_hidden_disk */ 431 test_blk_read(top_blk, 0x11, IMG_SIZE / 2, 432 IMG_SIZE / 2, 0, IMG_SIZE, false); 433 434 /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ 435 test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); 436 437 /* do active commit */ 438 replication_stop_all(failover, &error_abort); 439 440 /* read from s_local_disk (0, IMG_SIZE / 2) */ 441 test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, 442 0, IMG_SIZE / 2, false); 443 444 445 /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 446 test_blk_read(top_blk, 0x22, IMG_SIZE / 2, 447 IMG_SIZE / 2, 0, IMG_SIZE, false); 448 449 teardown_secondary(); 450 } 451 452 static void test_secondary_continuous_replication(void) 453 { 454 BlockBackend *top_blk, *local_blk; 455 456 top_blk = start_secondary(); 457 replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); 458 459 /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 460 local_blk = blk_by_name(S_LOCAL_DISK_ID); 461 test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); 462 463 /* replication will backup s_local_disk to s_hidden_disk */ 464 test_blk_read(top_blk, 0x11, IMG_SIZE / 2, 465 IMG_SIZE / 2, 0, IMG_SIZE, false); 466 467 /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ 468 test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); 469 470 /* do failover (active commit) */ 471 replication_stop_all(true, &error_abort); 472 473 /* it should ignore all requests from now on */ 474 475 /* start after failover */ 476 replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); 477 478 /* checkpoint */ 479 replication_do_checkpoint_all(&error_abort); 480 481 /* stop */ 482 replication_stop_all(true, &error_abort); 483 484 /* read from s_local_disk (0, IMG_SIZE / 2) */ 485 test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, 486 0, IMG_SIZE / 2, false); 487 488 489 /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 490 test_blk_read(top_blk, 0x22, IMG_SIZE / 2, 491 IMG_SIZE / 2, 0, IMG_SIZE, false); 492 493 teardown_secondary(); 494 } 495 496 static void test_secondary_do_checkpoint(void) 497 { 498 BlockBackend *top_blk, *local_blk; 499 bool failover = true; 500 501 top_blk = start_secondary(); 502 replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); 503 504 /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ 505 local_blk = blk_by_name(S_LOCAL_DISK_ID); 506 test_blk_write(local_blk, 0x22, IMG_SIZE / 2, 507 IMG_SIZE / 2, false); 508 509 /* replication will backup s_local_disk to s_hidden_disk */ 510 test_blk_read(top_blk, 0x11, IMG_SIZE / 2, 511 IMG_SIZE / 2, 0, IMG_SIZE, false); 512 513 replication_do_checkpoint_all(&error_abort); 514 515 /* after checkpoint, read pattern 0x22 from s_local_disk */ 516 test_blk_read(top_blk, 0x22, IMG_SIZE / 2, 517 IMG_SIZE / 2, 0, IMG_SIZE, false); 518 519 /* unblock top_bs */ 520 replication_stop_all(failover, &error_abort); 521 522 teardown_secondary(); 523 } 524 525 static void test_secondary_get_error_all(void) 526 { 527 bool failover = true; 528 529 start_secondary(); 530 replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); 531 532 replication_get_error_all(&error_abort); 533 534 /* unblock top_bs */ 535 replication_stop_all(failover, &error_abort); 536 537 teardown_secondary(); 538 } 539 #endif 540 541 static void sigabrt_handler(int signo) 542 { 543 cleanup_imgs(); 544 } 545 546 static void setup_sigabrt_handler(void) 547 { 548 #ifdef _WIN32 549 signal(SIGABRT, sigabrt_handler); 550 #else 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 #endif 560 } 561 562 int main(int argc, char **argv) 563 { 564 int ret; 565 const char *tmpdir = g_get_tmp_dir(); 566 p_local_disk = g_strdup_printf("%s/p_local_disk.XXXXXX", tmpdir); 567 s_local_disk = g_strdup_printf("%s/s_local_disk.XXXXXX", tmpdir); 568 s_active_disk = g_strdup_printf("%s/s_active_disk.XXXXXX", tmpdir); 569 s_hidden_disk = g_strdup_printf("%s/s_hidden_disk.XXXXXX", tmpdir); 570 qemu_init_main_loop(&error_fatal); 571 bdrv_init(); 572 573 g_test_init(&argc, &argv, NULL); 574 setup_sigabrt_handler(); 575 576 prepare_imgs(); 577 578 /* Primary */ 579 g_test_add_func("/replication/primary/read", test_primary_read); 580 g_test_add_func("/replication/primary/write", test_primary_write); 581 g_test_add_func("/replication/primary/start", test_primary_start); 582 g_test_add_func("/replication/primary/stop", test_primary_stop); 583 g_test_add_func("/replication/primary/do_checkpoint", 584 test_primary_do_checkpoint); 585 g_test_add_func("/replication/primary/get_error_all", 586 test_primary_get_error_all); 587 588 /* Secondary */ 589 g_test_add_func("/replication/secondary/read", test_secondary_read); 590 g_test_add_func("/replication/secondary/write", test_secondary_write); 591 #ifndef _WIN32 592 g_test_add_func("/replication/secondary/start", test_secondary_start); 593 g_test_add_func("/replication/secondary/stop", test_secondary_stop); 594 g_test_add_func("/replication/secondary/continuous_replication", 595 test_secondary_continuous_replication); 596 g_test_add_func("/replication/secondary/do_checkpoint", 597 test_secondary_do_checkpoint); 598 g_test_add_func("/replication/secondary/get_error_all", 599 test_secondary_get_error_all); 600 #endif 601 602 ret = g_test_run(); 603 604 cleanup_imgs(); 605 606 g_free(p_local_disk); 607 g_free(s_local_disk); 608 g_free(s_active_disk); 609 g_free(s_hidden_disk); 610 611 return ret; 612 } 613