xref: /openbmc/qemu/tests/unit/test-replication.c (revision 6016b7b4)
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 }
624