]> Git Repo - qemu.git/blob - block/replication.c
Merge remote-tracking branch 'remotes/xtensa/tags/20200625-xtensa' into staging
[qemu.git] / block / replication.c
1 /*
2  * Replication Block filter
3  *
4  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
5  * Copyright (c) 2016 Intel Corporation
6  * Copyright (c) 2016 FUJITSU LIMITED
7  *
8  * Author:
9  *   Wen Congyang <[email protected]>
10  *
11  * This work is licensed under the terms of the GNU GPL, version 2 or later.
12  * See the COPYING file in the top-level directory.
13  */
14
15 #include "qemu/osdep.h"
16 #include "qemu/module.h"
17 #include "qemu/option.h"
18 #include "block/nbd.h"
19 #include "block/blockjob.h"
20 #include "block/block_int.h"
21 #include "block/block_backup.h"
22 #include "sysemu/block-backend.h"
23 #include "qapi/error.h"
24 #include "qapi/qmp/qdict.h"
25 #include "replication.h"
26
27 typedef enum {
28     BLOCK_REPLICATION_NONE,             /* block replication is not started */
29     BLOCK_REPLICATION_RUNNING,          /* block replication is running */
30     BLOCK_REPLICATION_FAILOVER,         /* failover is running in background */
31     BLOCK_REPLICATION_FAILOVER_FAILED,  /* failover failed */
32     BLOCK_REPLICATION_DONE,             /* block replication is done */
33 } ReplicationStage;
34
35 typedef struct BDRVReplicationState {
36     ReplicationMode mode;
37     ReplicationStage stage;
38     BdrvChild *active_disk;
39     BlockJob *commit_job;
40     BdrvChild *hidden_disk;
41     BdrvChild *secondary_disk;
42     BlockJob *backup_job;
43     char *top_id;
44     ReplicationState *rs;
45     Error *blocker;
46     bool orig_hidden_read_only;
47     bool orig_secondary_read_only;
48     int error;
49 } BDRVReplicationState;
50
51 static void replication_start(ReplicationState *rs, ReplicationMode mode,
52                               Error **errp);
53 static void replication_do_checkpoint(ReplicationState *rs, Error **errp);
54 static void replication_get_error(ReplicationState *rs, Error **errp);
55 static void replication_stop(ReplicationState *rs, bool failover,
56                              Error **errp);
57
58 #define REPLICATION_MODE        "mode"
59 #define REPLICATION_TOP_ID      "top-id"
60 static QemuOptsList replication_runtime_opts = {
61     .name = "replication",
62     .head = QTAILQ_HEAD_INITIALIZER(replication_runtime_opts.head),
63     .desc = {
64         {
65             .name = REPLICATION_MODE,
66             .type = QEMU_OPT_STRING,
67         },
68         {
69             .name = REPLICATION_TOP_ID,
70             .type = QEMU_OPT_STRING,
71         },
72         { /* end of list */ }
73     },
74 };
75
76 static ReplicationOps replication_ops = {
77     .start = replication_start,
78     .checkpoint = replication_do_checkpoint,
79     .get_error = replication_get_error,
80     .stop = replication_stop,
81 };
82
83 static int replication_open(BlockDriverState *bs, QDict *options,
84                             int flags, Error **errp)
85 {
86     int ret;
87     BDRVReplicationState *s = bs->opaque;
88     Error *local_err = NULL;
89     QemuOpts *opts = NULL;
90     const char *mode;
91     const char *top_id;
92
93     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
94                                BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
95                                false, errp);
96     if (!bs->file) {
97         return -EINVAL;
98     }
99
100     ret = -EINVAL;
101     opts = qemu_opts_create(&replication_runtime_opts, NULL, 0, &error_abort);
102     qemu_opts_absorb_qdict(opts, options, &local_err);
103     if (local_err) {
104         goto fail;
105     }
106
107     mode = qemu_opt_get(opts, REPLICATION_MODE);
108     if (!mode) {
109         error_setg(&local_err, "Missing the option mode");
110         goto fail;
111     }
112
113     if (!strcmp(mode, "primary")) {
114         s->mode = REPLICATION_MODE_PRIMARY;
115         top_id = qemu_opt_get(opts, REPLICATION_TOP_ID);
116         if (top_id) {
117             error_setg(&local_err, "The primary side does not support option top-id");
118             goto fail;
119         }
120     } else if (!strcmp(mode, "secondary")) {
121         s->mode = REPLICATION_MODE_SECONDARY;
122         top_id = qemu_opt_get(opts, REPLICATION_TOP_ID);
123         s->top_id = g_strdup(top_id);
124         if (!s->top_id) {
125             error_setg(&local_err, "Missing the option top-id");
126             goto fail;
127         }
128     } else {
129         error_setg(&local_err,
130                    "The option mode's value should be primary or secondary");
131         goto fail;
132     }
133
134     s->rs = replication_new(bs, &replication_ops);
135
136     ret = 0;
137
138 fail:
139     qemu_opts_del(opts);
140     error_propagate(errp, local_err);
141
142     return ret;
143 }
144
145 static void replication_close(BlockDriverState *bs)
146 {
147     BDRVReplicationState *s = bs->opaque;
148     Job *commit_job;
149
150     if (s->stage == BLOCK_REPLICATION_RUNNING) {
151         replication_stop(s->rs, false, NULL);
152     }
153     if (s->stage == BLOCK_REPLICATION_FAILOVER) {
154         commit_job = &s->commit_job->job;
155         assert(commit_job->aio_context == qemu_get_current_aio_context());
156         job_cancel_sync(commit_job);
157     }
158
159     if (s->mode == REPLICATION_MODE_SECONDARY) {
160         g_free(s->top_id);
161     }
162
163     replication_remove(s->rs);
164 }
165
166 static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
167                                    BdrvChildRole role,
168                                    BlockReopenQueue *reopen_queue,
169                                    uint64_t perm, uint64_t shared,
170                                    uint64_t *nperm, uint64_t *nshared)
171 {
172     *nperm = BLK_PERM_CONSISTENT_READ;
173     if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) {
174         *nperm |= BLK_PERM_WRITE;
175     }
176     *nshared = BLK_PERM_CONSISTENT_READ
177                | BLK_PERM_WRITE
178                | BLK_PERM_WRITE_UNCHANGED;
179     return;
180 }
181
182 static int64_t replication_getlength(BlockDriverState *bs)
183 {
184     return bdrv_getlength(bs->file->bs);
185 }
186
187 static int replication_get_io_status(BDRVReplicationState *s)
188 {
189     switch (s->stage) {
190     case BLOCK_REPLICATION_NONE:
191         return -EIO;
192     case BLOCK_REPLICATION_RUNNING:
193         return 0;
194     case BLOCK_REPLICATION_FAILOVER:
195         return s->mode == REPLICATION_MODE_PRIMARY ? -EIO : 0;
196     case BLOCK_REPLICATION_FAILOVER_FAILED:
197         return s->mode == REPLICATION_MODE_PRIMARY ? -EIO : 1;
198     case BLOCK_REPLICATION_DONE:
199         /*
200          * active commit job completes, and active disk and secondary_disk
201          * is swapped, so we can operate bs->file directly
202          */
203         return s->mode == REPLICATION_MODE_PRIMARY ? -EIO : 0;
204     default:
205         abort();
206     }
207 }
208
209 static int replication_return_value(BDRVReplicationState *s, int ret)
210 {
211     if (s->mode == REPLICATION_MODE_SECONDARY) {
212         return ret;
213     }
214
215     if (ret < 0) {
216         s->error = ret;
217         ret = 0;
218     }
219
220     return ret;
221 }
222
223 static coroutine_fn int replication_co_readv(BlockDriverState *bs,
224                                              int64_t sector_num,
225                                              int remaining_sectors,
226                                              QEMUIOVector *qiov)
227 {
228     BDRVReplicationState *s = bs->opaque;
229     int ret;
230
231     if (s->mode == REPLICATION_MODE_PRIMARY) {
232         /* We only use it to forward primary write requests */
233         return -EIO;
234     }
235
236     ret = replication_get_io_status(s);
237     if (ret < 0) {
238         return ret;
239     }
240
241     ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
242                          remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
243
244     return replication_return_value(s, ret);
245 }
246
247 static coroutine_fn int replication_co_writev(BlockDriverState *bs,
248                                               int64_t sector_num,
249                                               int remaining_sectors,
250                                               QEMUIOVector *qiov,
251                                               int flags)
252 {
253     BDRVReplicationState *s = bs->opaque;
254     QEMUIOVector hd_qiov;
255     uint64_t bytes_done = 0;
256     BdrvChild *top = bs->file;
257     BdrvChild *base = s->secondary_disk;
258     BdrvChild *target;
259     int ret;
260     int64_t n;
261
262     assert(!flags);
263     ret = replication_get_io_status(s);
264     if (ret < 0) {
265         goto out;
266     }
267
268     if (ret == 0) {
269         ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE,
270                               remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
271         return replication_return_value(s, ret);
272     }
273
274     /*
275      * Failover failed, only write to active disk if the sectors
276      * have already been allocated in active disk/hidden disk.
277      */
278     qemu_iovec_init(&hd_qiov, qiov->niov);
279     while (remaining_sectors > 0) {
280         int64_t count;
281
282         ret = bdrv_is_allocated_above(top->bs, base->bs, false,
283                                       sector_num * BDRV_SECTOR_SIZE,
284                                       remaining_sectors * BDRV_SECTOR_SIZE,
285                                       &count);
286         if (ret < 0) {
287             goto out1;
288         }
289
290         assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
291         n = count >> BDRV_SECTOR_BITS;
292         qemu_iovec_reset(&hd_qiov);
293         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
294
295         target = ret ? top : base;
296         ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE,
297                               n * BDRV_SECTOR_SIZE, &hd_qiov, 0);
298         if (ret < 0) {
299             goto out1;
300         }
301
302         remaining_sectors -= n;
303         sector_num += n;
304         bytes_done += count;
305     }
306
307 out1:
308     qemu_iovec_destroy(&hd_qiov);
309 out:
310     return ret;
311 }
312
313 static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
314 {
315     Error *local_err = NULL;
316     int ret;
317
318     if (!s->backup_job) {
319         error_setg(errp, "Backup job was cancelled unexpectedly");
320         return;
321     }
322
323     backup_do_checkpoint(s->backup_job, &local_err);
324     if (local_err) {
325         error_propagate(errp, local_err);
326         return;
327     }
328
329     if (!s->active_disk->bs->drv) {
330         error_setg(errp, "Active disk %s is ejected",
331                    s->active_disk->bs->node_name);
332         return;
333     }
334
335     ret = bdrv_make_empty(s->active_disk, errp);
336     if (ret < 0) {
337         return;
338     }
339
340     if (!s->hidden_disk->bs->drv) {
341         error_setg(errp, "Hidden disk %s is ejected",
342                    s->hidden_disk->bs->node_name);
343         return;
344     }
345
346     BlockBackend *blk = blk_new(qemu_get_current_aio_context(),
347                                 BLK_PERM_WRITE, BLK_PERM_ALL);
348     blk_insert_bs(blk, s->hidden_disk->bs, &local_err);
349     if (local_err) {
350         error_propagate(errp, local_err);
351         blk_unref(blk);
352         return;
353     }
354
355     ret = blk_make_empty(blk, errp);
356     blk_unref(blk);
357     if (ret < 0) {
358         return;
359     }
360 }
361
362 /* This function is supposed to be called twice:
363  * first with writable = true, then with writable = false.
364  * The first call puts s->hidden_disk and s->secondary_disk in
365  * r/w mode, and the second puts them back in their original state.
366  */
367 static void reopen_backing_file(BlockDriverState *bs, bool writable,
368                                 Error **errp)
369 {
370     BDRVReplicationState *s = bs->opaque;
371     BlockReopenQueue *reopen_queue = NULL;
372     Error *local_err = NULL;
373
374     if (writable) {
375         s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs);
376         s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs);
377     }
378
379     bdrv_subtree_drained_begin(s->hidden_disk->bs);
380     bdrv_subtree_drained_begin(s->secondary_disk->bs);
381
382     if (s->orig_hidden_read_only) {
383         QDict *opts = qdict_new();
384         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
385         reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
386                                          opts, true);
387     }
388
389     if (s->orig_secondary_read_only) {
390         QDict *opts = qdict_new();
391         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
392         reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
393                                          opts, true);
394     }
395
396     if (reopen_queue) {
397         bdrv_reopen_multiple(reopen_queue, &local_err);
398         error_propagate(errp, local_err);
399     }
400
401     bdrv_subtree_drained_end(s->hidden_disk->bs);
402     bdrv_subtree_drained_end(s->secondary_disk->bs);
403 }
404
405 static void backup_job_cleanup(BlockDriverState *bs)
406 {
407     BDRVReplicationState *s = bs->opaque;
408     BlockDriverState *top_bs;
409
410     s->backup_job = NULL;
411
412     top_bs = bdrv_lookup_bs(s->top_id, s->top_id, NULL);
413     if (!top_bs) {
414         return;
415     }
416     bdrv_op_unblock_all(top_bs, s->blocker);
417     error_free(s->blocker);
418     reopen_backing_file(bs, false, NULL);
419 }
420
421 static void backup_job_completed(void *opaque, int ret)
422 {
423     BlockDriverState *bs = opaque;
424     BDRVReplicationState *s = bs->opaque;
425
426     if (s->stage != BLOCK_REPLICATION_FAILOVER) {
427         /* The backup job is cancelled unexpectedly */
428         s->error = -EIO;
429     }
430
431     backup_job_cleanup(bs);
432 }
433
434 static bool check_top_bs(BlockDriverState *top_bs, BlockDriverState *bs)
435 {
436     BdrvChild *child;
437
438     /* The bs itself is the top_bs */
439     if (top_bs == bs) {
440         return true;
441     }
442
443     /* Iterate over top_bs's children */
444     QLIST_FOREACH(child, &top_bs->children, next) {
445         if (child->bs == bs || check_top_bs(child->bs, bs)) {
446             return true;
447         }
448     }
449
450     return false;
451 }
452
453 static void replication_start(ReplicationState *rs, ReplicationMode mode,
454                               Error **errp)
455 {
456     BlockDriverState *bs = rs->opaque;
457     BDRVReplicationState *s;
458     BlockDriverState *top_bs;
459     int64_t active_length, hidden_length, disk_length;
460     AioContext *aio_context;
461     Error *local_err = NULL;
462
463     aio_context = bdrv_get_aio_context(bs);
464     aio_context_acquire(aio_context);
465     s = bs->opaque;
466
467     if (s->stage == BLOCK_REPLICATION_DONE ||
468         s->stage == BLOCK_REPLICATION_FAILOVER) {
469         /*
470          * This case happens when a secondary is promoted to primary.
471          * Ignore the request because the secondary side of replication
472          * doesn't have to do anything anymore.
473          */
474         aio_context_release(aio_context);
475         return;
476     }
477
478     if (s->stage != BLOCK_REPLICATION_NONE) {
479         error_setg(errp, "Block replication is running or done");
480         aio_context_release(aio_context);
481         return;
482     }
483
484     if (s->mode != mode) {
485         error_setg(errp, "The parameter mode's value is invalid, needs %d,"
486                    " but got %d", s->mode, mode);
487         aio_context_release(aio_context);
488         return;
489     }
490
491     switch (s->mode) {
492     case REPLICATION_MODE_PRIMARY:
493         break;
494     case REPLICATION_MODE_SECONDARY:
495         s->active_disk = bs->file;
496         if (!s->active_disk || !s->active_disk->bs ||
497                                     !s->active_disk->bs->backing) {
498             error_setg(errp, "Active disk doesn't have backing file");
499             aio_context_release(aio_context);
500             return;
501         }
502
503         s->hidden_disk = s->active_disk->bs->backing;
504         if (!s->hidden_disk->bs || !s->hidden_disk->bs->backing) {
505             error_setg(errp, "Hidden disk doesn't have backing file");
506             aio_context_release(aio_context);
507             return;
508         }
509
510         s->secondary_disk = s->hidden_disk->bs->backing;
511         if (!s->secondary_disk->bs || !bdrv_has_blk(s->secondary_disk->bs)) {
512             error_setg(errp, "The secondary disk doesn't have block backend");
513             aio_context_release(aio_context);
514             return;
515         }
516
517         /* verify the length */
518         active_length = bdrv_getlength(s->active_disk->bs);
519         hidden_length = bdrv_getlength(s->hidden_disk->bs);
520         disk_length = bdrv_getlength(s->secondary_disk->bs);
521         if (active_length < 0 || hidden_length < 0 || disk_length < 0 ||
522             active_length != hidden_length || hidden_length != disk_length) {
523             error_setg(errp, "Active disk, hidden disk, secondary disk's length"
524                        " are not the same");
525             aio_context_release(aio_context);
526             return;
527         }
528
529         /* Must be true, or the bdrv_getlength() calls would have failed */
530         assert(s->active_disk->bs->drv && s->hidden_disk->bs->drv);
531
532         if (!s->active_disk->bs->drv->bdrv_make_empty ||
533             !s->hidden_disk->bs->drv->bdrv_make_empty) {
534             error_setg(errp,
535                        "Active disk or hidden disk doesn't support make_empty");
536             aio_context_release(aio_context);
537             return;
538         }
539
540         /* reopen the backing file in r/w mode */
541         reopen_backing_file(bs, true, &local_err);
542         if (local_err) {
543             error_propagate(errp, local_err);
544             aio_context_release(aio_context);
545             return;
546         }
547
548         /* start backup job now */
549         error_setg(&s->blocker,
550                    "Block device is in use by internal backup job");
551
552         top_bs = bdrv_lookup_bs(s->top_id, s->top_id, NULL);
553         if (!top_bs || !bdrv_is_root_node(top_bs) ||
554             !check_top_bs(top_bs, bs)) {
555             error_setg(errp, "No top_bs or it is invalid");
556             reopen_backing_file(bs, false, NULL);
557             aio_context_release(aio_context);
558             return;
559         }
560         bdrv_op_block_all(top_bs, s->blocker);
561         bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
562
563         s->backup_job = backup_job_create(
564                                 NULL, s->secondary_disk->bs, s->hidden_disk->bs,
565                                 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
566                                 BLOCKDEV_ON_ERROR_REPORT,
567                                 BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
568                                 backup_job_completed, bs, NULL, &local_err);
569         if (local_err) {
570             error_propagate(errp, local_err);
571             backup_job_cleanup(bs);
572             aio_context_release(aio_context);
573             return;
574         }
575         job_start(&s->backup_job->job);
576         break;
577     default:
578         aio_context_release(aio_context);
579         abort();
580     }
581
582     s->stage = BLOCK_REPLICATION_RUNNING;
583
584     if (s->mode == REPLICATION_MODE_SECONDARY) {
585         secondary_do_checkpoint(s, errp);
586     }
587
588     s->error = 0;
589     aio_context_release(aio_context);
590 }
591
592 static void replication_do_checkpoint(ReplicationState *rs, Error **errp)
593 {
594     BlockDriverState *bs = rs->opaque;
595     BDRVReplicationState *s;
596     AioContext *aio_context;
597
598     aio_context = bdrv_get_aio_context(bs);
599     aio_context_acquire(aio_context);
600     s = bs->opaque;
601
602     if (s->stage == BLOCK_REPLICATION_DONE ||
603         s->stage == BLOCK_REPLICATION_FAILOVER) {
604         /*
605          * This case happens when a secondary was promoted to primary.
606          * Ignore the request because the secondary side of replication
607          * doesn't have to do anything anymore.
608          */
609         aio_context_release(aio_context);
610         return;
611     }
612
613     if (s->mode == REPLICATION_MODE_SECONDARY) {
614         secondary_do_checkpoint(s, errp);
615     }
616     aio_context_release(aio_context);
617 }
618
619 static void replication_get_error(ReplicationState *rs, Error **errp)
620 {
621     BlockDriverState *bs = rs->opaque;
622     BDRVReplicationState *s;
623     AioContext *aio_context;
624
625     aio_context = bdrv_get_aio_context(bs);
626     aio_context_acquire(aio_context);
627     s = bs->opaque;
628
629     if (s->stage == BLOCK_REPLICATION_NONE) {
630         error_setg(errp, "Block replication is not running");
631         aio_context_release(aio_context);
632         return;
633     }
634
635     if (s->error) {
636         error_setg(errp, "I/O error occurred");
637         aio_context_release(aio_context);
638         return;
639     }
640     aio_context_release(aio_context);
641 }
642
643 static void replication_done(void *opaque, int ret)
644 {
645     BlockDriverState *bs = opaque;
646     BDRVReplicationState *s = bs->opaque;
647
648     if (ret == 0) {
649         s->stage = BLOCK_REPLICATION_DONE;
650
651         s->active_disk = NULL;
652         s->secondary_disk = NULL;
653         s->hidden_disk = NULL;
654         s->error = 0;
655     } else {
656         s->stage = BLOCK_REPLICATION_FAILOVER_FAILED;
657         s->error = -EIO;
658     }
659 }
660
661 static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
662 {
663     BlockDriverState *bs = rs->opaque;
664     BDRVReplicationState *s;
665     AioContext *aio_context;
666
667     aio_context = bdrv_get_aio_context(bs);
668     aio_context_acquire(aio_context);
669     s = bs->opaque;
670
671     if (s->stage == BLOCK_REPLICATION_DONE ||
672         s->stage == BLOCK_REPLICATION_FAILOVER) {
673         /*
674          * This case happens when a secondary was promoted to primary.
675          * Ignore the request because the secondary side of replication
676          * doesn't have to do anything anymore.
677          */
678         aio_context_release(aio_context);
679         return;
680     }
681
682     if (s->stage != BLOCK_REPLICATION_RUNNING) {
683         error_setg(errp, "Block replication is not running");
684         aio_context_release(aio_context);
685         return;
686     }
687
688     switch (s->mode) {
689     case REPLICATION_MODE_PRIMARY:
690         s->stage = BLOCK_REPLICATION_DONE;
691         s->error = 0;
692         break;
693     case REPLICATION_MODE_SECONDARY:
694         /*
695          * This BDS will be closed, and the job should be completed
696          * before the BDS is closed, because we will access hidden
697          * disk, secondary disk in backup_job_completed().
698          */
699         if (s->backup_job) {
700             job_cancel_sync(&s->backup_job->job);
701         }
702
703         if (!failover) {
704             secondary_do_checkpoint(s, errp);
705             s->stage = BLOCK_REPLICATION_DONE;
706             aio_context_release(aio_context);
707             return;
708         }
709
710         s->stage = BLOCK_REPLICATION_FAILOVER;
711         s->commit_job = commit_active_start(
712                             NULL, s->active_disk->bs, s->secondary_disk->bs,
713                             JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
714                             NULL, replication_done, bs, true, errp);
715         break;
716     default:
717         aio_context_release(aio_context);
718         abort();
719     }
720     aio_context_release(aio_context);
721 }
722
723 static const char *const replication_strong_runtime_opts[] = {
724     REPLICATION_MODE,
725     REPLICATION_TOP_ID,
726
727     NULL
728 };
729
730 static BlockDriver bdrv_replication = {
731     .format_name                = "replication",
732     .instance_size              = sizeof(BDRVReplicationState),
733
734     .bdrv_open                  = replication_open,
735     .bdrv_close                 = replication_close,
736     .bdrv_child_perm            = replication_child_perm,
737
738     .bdrv_getlength             = replication_getlength,
739     .bdrv_co_readv              = replication_co_readv,
740     .bdrv_co_writev             = replication_co_writev,
741
742     .is_filter                  = true,
743
744     .has_variable_length        = true,
745     .strong_runtime_opts        = replication_strong_runtime_opts,
746 };
747
748 static void bdrv_replication_init(void)
749 {
750     bdrv_register(&bdrv_replication);
751 }
752
753 block_init(bdrv_replication_init);
This page took 0.066234 seconds and 4 git commands to generate.