]> Git Repo - qemu.git/blob - tests/test-replication.c
Merge remote-tracking branch 'remotes/armbru/tags/pull-error-2020-07-02' into staging
[qemu.git] / tests / test-replication.c
1 /*
2  * Block replication tests
3  *
4  * Copyright (c) 2016 FUJITSU LIMITED
5  * Author: Changlong Xie <[email protected]>
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 "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[] = "/tmp/p_local_disk.XXXXXX";
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[] = "/tmp/s_local_disk.XXXXXX";
32 static char s_active_disk[] = "/tmp/s_active_disk.XXXXXX";
33 static char s_hidden_disk[] = "/tmp/s_hidden_disk.XXXXXX";
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 static void test_secondary_start(void)
396 {
397     BlockBackend *top_blk, *local_blk;
398     bool failover = true;
399
400     top_blk = start_secondary();
401     replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort);
402
403     /* read from s_local_disk (0, IMG_SIZE) */
404     test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false);
405
406     /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
407     local_blk = blk_by_name(S_LOCAL_DISK_ID);
408     test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false);
409
410     /* replication will backup s_local_disk to s_hidden_disk */
411     test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
412                   IMG_SIZE / 2, 0, IMG_SIZE, false);
413
414     /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */
415     test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false);
416
417     /* read from s_active_disk (0, IMG_SIZE/2) */
418     test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2,
419                   0, IMG_SIZE / 2, false);
420
421     /* unblock top_bs */
422     replication_stop_all(failover, &error_abort);
423
424     teardown_secondary();
425 }
426
427
428 static void test_secondary_stop(void)
429 {
430     BlockBackend *top_blk, *local_blk;
431     bool failover = true;
432
433     top_blk = start_secondary();
434     replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort);
435
436     /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
437     local_blk = blk_by_name(S_LOCAL_DISK_ID);
438     test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false);
439
440     /* replication will backup s_local_disk to s_hidden_disk */
441     test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
442                   IMG_SIZE / 2, 0, IMG_SIZE, false);
443
444     /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */
445     test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false);
446
447     /* do active commit */
448     replication_stop_all(failover, &error_abort);
449
450     /* read from s_local_disk (0, IMG_SIZE / 2) */
451     test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2,
452                   0, IMG_SIZE / 2, false);
453
454
455     /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
456     test_blk_read(top_blk, 0x22, IMG_SIZE / 2,
457                   IMG_SIZE / 2, 0, IMG_SIZE, false);
458
459     teardown_secondary();
460 }
461
462 static void test_secondary_continuous_replication(void)
463 {
464     BlockBackend *top_blk, *local_blk;
465
466     top_blk = start_secondary();
467     replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort);
468
469     /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
470     local_blk = blk_by_name(S_LOCAL_DISK_ID);
471     test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false);
472
473     /* replication will backup s_local_disk to s_hidden_disk */
474     test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
475                   IMG_SIZE / 2, 0, IMG_SIZE, false);
476
477     /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */
478     test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false);
479
480     /* do failover (active commit) */
481     replication_stop_all(true, &error_abort);
482
483     /* it should ignore all requests from now on */
484
485     /* start after failover */
486     replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort);
487
488     /* checkpoint */
489     replication_do_checkpoint_all(&error_abort);
490
491     /* stop */
492     replication_stop_all(true, &error_abort);
493
494     /* read from s_local_disk (0, IMG_SIZE / 2) */
495     test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2,
496                   0, IMG_SIZE / 2, false);
497
498
499     /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
500     test_blk_read(top_blk, 0x22, IMG_SIZE / 2,
501                   IMG_SIZE / 2, 0, IMG_SIZE, false);
502
503     teardown_secondary();
504 }
505
506 static void test_secondary_do_checkpoint(void)
507 {
508     BlockBackend *top_blk, *local_blk;
509     bool failover = true;
510
511     top_blk = start_secondary();
512     replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort);
513
514     /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */
515     local_blk = blk_by_name(S_LOCAL_DISK_ID);
516     test_blk_write(local_blk, 0x22, IMG_SIZE / 2,
517                    IMG_SIZE / 2, false);
518
519     /* replication will backup s_local_disk to s_hidden_disk */
520     test_blk_read(top_blk, 0x11, IMG_SIZE / 2,
521                   IMG_SIZE / 2, 0, IMG_SIZE, false);
522
523     replication_do_checkpoint_all(&error_abort);
524
525     /* after checkpoint, read pattern 0x22 from s_local_disk */
526     test_blk_read(top_blk, 0x22, IMG_SIZE / 2,
527                   IMG_SIZE / 2, 0, IMG_SIZE, false);
528
529     /* unblock top_bs */
530     replication_stop_all(failover, &error_abort);
531
532     teardown_secondary();
533 }
534
535 static void test_secondary_get_error_all(void)
536 {
537     bool failover = true;
538
539     start_secondary();
540     replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort);
541
542     replication_get_error_all(&error_abort);
543
544     /* unblock top_bs */
545     replication_stop_all(failover, &error_abort);
546
547     teardown_secondary();
548 }
549
550 static void sigabrt_handler(int signo)
551 {
552     cleanup_imgs();
553 }
554
555 static void setup_sigabrt_handler(void)
556 {
557     struct sigaction sigact;
558
559     sigact = (struct sigaction) {
560         .sa_handler = sigabrt_handler,
561         .sa_flags = SA_RESETHAND,
562     };
563     sigemptyset(&sigact.sa_mask);
564     sigaction(SIGABRT, &sigact, NULL);
565 }
566
567 int main(int argc, char **argv)
568 {
569     int ret;
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     g_test_add_func("/replication/secondary/start", test_secondary_start);
592     g_test_add_func("/replication/secondary/stop",  test_secondary_stop);
593     g_test_add_func("/replication/secondary/continuous_replication",
594                     test_secondary_continuous_replication);
595     g_test_add_func("/replication/secondary/do_checkpoint",
596                     test_secondary_do_checkpoint);
597     g_test_add_func("/replication/secondary/get_error_all",
598                     test_secondary_get_error_all);
599
600     ret = g_test_run();
601
602     cleanup_imgs();
603
604     return ret;
605 }
This page took 0.057218 seconds and 4 git commands to generate.