xref: /openbmc/qemu/tests/unit/test-replication.c (revision 7618fffd)
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