]> Git Repo - qemu.git/blob - block-migration.c
build: move QAPI definitions for QEMU out of qapi-obj-y
[qemu.git] / block-migration.c
1 /*
2  * QEMU live block migration
3  *
4  * Copyright IBM, Corp. 2009
5  *
6  * Authors:
7  *  Liran Schour   <[email protected]>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  *
12  * Contributions after 2012-01-13 are licensed under the terms of the
13  * GNU GPL, version 2 or (at your option) any later version.
14  */
15
16 #include "qemu-common.h"
17 #include "block/block_int.h"
18 #include "hw/hw.h"
19 #include "qemu/queue.h"
20 #include "qemu/timer.h"
21 #include "migration/block.h"
22 #include "migration/migration.h"
23 #include "sysemu/blockdev.h"
24 #include <assert.h>
25
26 #define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
27
28 #define BLK_MIG_FLAG_DEVICE_BLOCK       0x01
29 #define BLK_MIG_FLAG_EOS                0x02
30 #define BLK_MIG_FLAG_PROGRESS           0x04
31
32 #define MAX_IS_ALLOCATED_SEARCH 65536
33
34 //#define DEBUG_BLK_MIGRATION
35
36 #ifdef DEBUG_BLK_MIGRATION
37 #define DPRINTF(fmt, ...) \
38     do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0)
39 #else
40 #define DPRINTF(fmt, ...) \
41     do { } while (0)
42 #endif
43
44 typedef struct BlkMigDevState {
45     BlockDriverState *bs;
46     int bulk_completed;
47     int shared_base;
48     int64_t cur_sector;
49     int64_t cur_dirty;
50     int64_t completed_sectors;
51     int64_t total_sectors;
52     int64_t dirty;
53     QSIMPLEQ_ENTRY(BlkMigDevState) entry;
54     unsigned long *aio_bitmap;
55 } BlkMigDevState;
56
57 typedef struct BlkMigBlock {
58     uint8_t *buf;
59     BlkMigDevState *bmds;
60     int64_t sector;
61     int nr_sectors;
62     struct iovec iov;
63     QEMUIOVector qiov;
64     BlockDriverAIOCB *aiocb;
65     int ret;
66     QSIMPLEQ_ENTRY(BlkMigBlock) entry;
67 } BlkMigBlock;
68
69 typedef struct BlkMigState {
70     int blk_enable;
71     int shared_base;
72     QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list;
73     QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list;
74     int submitted;
75     int read_done;
76     int transferred;
77     int64_t total_sector_sum;
78     int prev_progress;
79     int bulk_completed;
80     long double prev_time_offset;
81 } BlkMigState;
82
83 static BlkMigState block_mig_state;
84
85 static void blk_send(QEMUFile *f, BlkMigBlock * blk)
86 {
87     int len;
88
89     /* sector number and flags */
90     qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS)
91                      | BLK_MIG_FLAG_DEVICE_BLOCK);
92
93     /* device name */
94     len = strlen(blk->bmds->bs->device_name);
95     qemu_put_byte(f, len);
96     qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len);
97
98     qemu_put_buffer(f, blk->buf, BLOCK_SIZE);
99 }
100
101 int blk_mig_active(void)
102 {
103     return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list);
104 }
105
106 uint64_t blk_mig_bytes_transferred(void)
107 {
108     BlkMigDevState *bmds;
109     uint64_t sum = 0;
110
111     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
112         sum += bmds->completed_sectors;
113     }
114     return sum << BDRV_SECTOR_BITS;
115 }
116
117 uint64_t blk_mig_bytes_remaining(void)
118 {
119     return blk_mig_bytes_total() - blk_mig_bytes_transferred();
120 }
121
122 uint64_t blk_mig_bytes_total(void)
123 {
124     BlkMigDevState *bmds;
125     uint64_t sum = 0;
126
127     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
128         sum += bmds->total_sectors;
129     }
130     return sum << BDRV_SECTOR_BITS;
131 }
132
133 static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
134 {
135     int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
136
137     if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) {
138         return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
139             (1UL << (chunk % (sizeof(unsigned long) * 8))));
140     } else {
141         return 0;
142     }
143 }
144
145 static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
146                              int nb_sectors, int set)
147 {
148     int64_t start, end;
149     unsigned long val, idx, bit;
150
151     start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
152     end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
153
154     for (; start <= end; start++) {
155         idx = start / (sizeof(unsigned long) * 8);
156         bit = start % (sizeof(unsigned long) * 8);
157         val = bmds->aio_bitmap[idx];
158         if (set) {
159             val |= 1UL << bit;
160         } else {
161             val &= ~(1UL << bit);
162         }
163         bmds->aio_bitmap[idx] = val;
164     }
165 }
166
167 static void alloc_aio_bitmap(BlkMigDevState *bmds)
168 {
169     BlockDriverState *bs = bmds->bs;
170     int64_t bitmap_size;
171
172     bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
173             BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
174     bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
175
176     bmds->aio_bitmap = g_malloc0(bitmap_size);
177 }
178
179 static void blk_mig_read_cb(void *opaque, int ret)
180 {
181     long double curr_time = qemu_get_clock_ns(rt_clock);
182     BlkMigBlock *blk = opaque;
183
184     blk->ret = ret;
185
186     block_mig_state.prev_time_offset = curr_time;
187
188     QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
189     bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
190
191     block_mig_state.submitted--;
192     block_mig_state.read_done++;
193     assert(block_mig_state.submitted >= 0);
194 }
195
196 static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
197 {
198     int64_t total_sectors = bmds->total_sectors;
199     int64_t cur_sector = bmds->cur_sector;
200     BlockDriverState *bs = bmds->bs;
201     BlkMigBlock *blk;
202     int nr_sectors;
203
204     if (bmds->shared_base) {
205         while (cur_sector < total_sectors &&
206                !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH,
207                                   &nr_sectors)) {
208             cur_sector += nr_sectors;
209         }
210     }
211
212     if (cur_sector >= total_sectors) {
213         bmds->cur_sector = bmds->completed_sectors = total_sectors;
214         return 1;
215     }
216
217     bmds->completed_sectors = cur_sector;
218
219     cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1);
220
221     /* we are going to transfer a full block even if it is not allocated */
222     nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
223
224     if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
225         nr_sectors = total_sectors - cur_sector;
226     }
227
228     blk = g_malloc(sizeof(BlkMigBlock));
229     blk->buf = g_malloc(BLOCK_SIZE);
230     blk->bmds = bmds;
231     blk->sector = cur_sector;
232     blk->nr_sectors = nr_sectors;
233
234     blk->iov.iov_base = blk->buf;
235     blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
236     qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
237
238     if (block_mig_state.submitted == 0) {
239         block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
240     }
241
242     blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
243                                 nr_sectors, blk_mig_read_cb, blk);
244     block_mig_state.submitted++;
245
246     bdrv_reset_dirty(bs, cur_sector, nr_sectors);
247     bmds->cur_sector = cur_sector + nr_sectors;
248
249     return (bmds->cur_sector >= total_sectors);
250 }
251
252 static void set_dirty_tracking(int enable)
253 {
254     BlkMigDevState *bmds;
255
256     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
257         bdrv_set_dirty_tracking(bmds->bs, enable);
258     }
259 }
260
261 static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
262 {
263     BlkMigDevState *bmds;
264     int64_t sectors;
265
266     if (!bdrv_is_read_only(bs)) {
267         sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
268         if (sectors <= 0) {
269             return;
270         }
271
272         bmds = g_malloc0(sizeof(BlkMigDevState));
273         bmds->bs = bs;
274         bmds->bulk_completed = 0;
275         bmds->total_sectors = sectors;
276         bmds->completed_sectors = 0;
277         bmds->shared_base = block_mig_state.shared_base;
278         alloc_aio_bitmap(bmds);
279         drive_get_ref(drive_get_by_blockdev(bs));
280         bdrv_set_in_use(bs, 1);
281
282         block_mig_state.total_sector_sum += sectors;
283
284         if (bmds->shared_base) {
285             DPRINTF("Start migration for %s with shared base image\n",
286                     bs->device_name);
287         } else {
288             DPRINTF("Start full migration for %s\n", bs->device_name);
289         }
290
291         QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
292     }
293 }
294
295 static void init_blk_migration(QEMUFile *f)
296 {
297     block_mig_state.submitted = 0;
298     block_mig_state.read_done = 0;
299     block_mig_state.transferred = 0;
300     block_mig_state.total_sector_sum = 0;
301     block_mig_state.prev_progress = -1;
302     block_mig_state.bulk_completed = 0;
303
304     bdrv_iterate(init_blk_migration_it, NULL);
305 }
306
307 static int blk_mig_save_bulked_block(QEMUFile *f)
308 {
309     int64_t completed_sector_sum = 0;
310     BlkMigDevState *bmds;
311     int progress;
312     int ret = 0;
313
314     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
315         if (bmds->bulk_completed == 0) {
316             if (mig_save_device_bulk(f, bmds) == 1) {
317                 /* completed bulk section for this device */
318                 bmds->bulk_completed = 1;
319             }
320             completed_sector_sum += bmds->completed_sectors;
321             ret = 1;
322             break;
323         } else {
324             completed_sector_sum += bmds->completed_sectors;
325         }
326     }
327
328     if (block_mig_state.total_sector_sum != 0) {
329         progress = completed_sector_sum * 100 /
330                    block_mig_state.total_sector_sum;
331     } else {
332         progress = 100;
333     }
334     if (progress != block_mig_state.prev_progress) {
335         block_mig_state.prev_progress = progress;
336         qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
337                          | BLK_MIG_FLAG_PROGRESS);
338         DPRINTF("Completed %d %%\r", progress);
339     }
340
341     return ret;
342 }
343
344 static void blk_mig_reset_dirty_cursor(void)
345 {
346     BlkMigDevState *bmds;
347
348     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
349         bmds->cur_dirty = 0;
350     }
351 }
352
353 static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
354                                  int is_async)
355 {
356     BlkMigBlock *blk;
357     int64_t total_sectors = bmds->total_sectors;
358     int64_t sector;
359     int nr_sectors;
360     int ret = -EIO;
361
362     for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
363         if (bmds_aio_inflight(bmds, sector)) {
364             bdrv_drain_all();
365         }
366         if (bdrv_get_dirty(bmds->bs, sector)) {
367
368             if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
369                 nr_sectors = total_sectors - sector;
370             } else {
371                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
372             }
373             blk = g_malloc(sizeof(BlkMigBlock));
374             blk->buf = g_malloc(BLOCK_SIZE);
375             blk->bmds = bmds;
376             blk->sector = sector;
377             blk->nr_sectors = nr_sectors;
378
379             if (is_async) {
380                 blk->iov.iov_base = blk->buf;
381                 blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
382                 qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
383
384                 if (block_mig_state.submitted == 0) {
385                     block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
386                 }
387
388                 blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
389                                             nr_sectors, blk_mig_read_cb, blk);
390                 block_mig_state.submitted++;
391                 bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
392             } else {
393                 ret = bdrv_read(bmds->bs, sector, blk->buf, nr_sectors);
394                 if (ret < 0) {
395                     goto error;
396                 }
397                 blk_send(f, blk);
398
399                 g_free(blk->buf);
400                 g_free(blk);
401             }
402
403             bdrv_reset_dirty(bmds->bs, sector, nr_sectors);
404             break;
405         }
406         sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
407         bmds->cur_dirty = sector;
408     }
409
410     return (bmds->cur_dirty >= bmds->total_sectors);
411
412 error:
413     DPRINTF("Error reading sector %" PRId64 "\n", sector);
414     g_free(blk->buf);
415     g_free(blk);
416     return ret;
417 }
418
419 /* return value:
420  * 0: too much data for max_downtime
421  * 1: few enough data for max_downtime
422 */
423 static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
424 {
425     BlkMigDevState *bmds;
426     int ret = 1;
427
428     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
429         ret = mig_save_device_dirty(f, bmds, is_async);
430         if (ret <= 0) {
431             break;
432         }
433     }
434
435     return ret;
436 }
437
438 static int flush_blks(QEMUFile *f)
439 {
440     BlkMigBlock *blk;
441     int ret = 0;
442
443     DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
444             __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
445             block_mig_state.transferred);
446
447     while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
448         if (qemu_file_rate_limit(f)) {
449             break;
450         }
451         if (blk->ret < 0) {
452             ret = blk->ret;
453             break;
454         }
455         blk_send(f, blk);
456
457         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
458         g_free(blk->buf);
459         g_free(blk);
460
461         block_mig_state.read_done--;
462         block_mig_state.transferred++;
463         assert(block_mig_state.read_done >= 0);
464     }
465
466     DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
467             block_mig_state.submitted, block_mig_state.read_done,
468             block_mig_state.transferred);
469     return ret;
470 }
471
472 static int64_t get_remaining_dirty(void)
473 {
474     BlkMigDevState *bmds;
475     int64_t dirty = 0;
476
477     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
478         dirty += bdrv_get_dirty_count(bmds->bs);
479     }
480
481     return dirty * BLOCK_SIZE;
482 }
483
484 static void blk_mig_cleanup(void)
485 {
486     BlkMigDevState *bmds;
487     BlkMigBlock *blk;
488
489     bdrv_drain_all();
490
491     set_dirty_tracking(0);
492
493     while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
494         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
495         bdrv_set_in_use(bmds->bs, 0);
496         drive_put_ref(drive_get_by_blockdev(bmds->bs));
497         g_free(bmds->aio_bitmap);
498         g_free(bmds);
499     }
500
501     while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
502         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
503         g_free(blk->buf);
504         g_free(blk);
505     }
506 }
507
508 static void block_migration_cancel(void *opaque)
509 {
510     blk_mig_cleanup();
511 }
512
513 static int block_save_setup(QEMUFile *f, void *opaque)
514 {
515     int ret;
516
517     DPRINTF("Enter save live setup submitted %d transferred %d\n",
518             block_mig_state.submitted, block_mig_state.transferred);
519
520     init_blk_migration(f);
521
522     /* start track dirty blocks */
523     set_dirty_tracking(1);
524
525     ret = flush_blks(f);
526     if (ret) {
527         blk_mig_cleanup();
528         return ret;
529     }
530
531     blk_mig_reset_dirty_cursor();
532
533     qemu_put_be64(f, BLK_MIG_FLAG_EOS);
534
535     return 0;
536 }
537
538 static int block_save_iterate(QEMUFile *f, void *opaque)
539 {
540     int ret;
541
542     DPRINTF("Enter save live iterate submitted %d transferred %d\n",
543             block_mig_state.submitted, block_mig_state.transferred);
544
545     ret = flush_blks(f);
546     if (ret) {
547         blk_mig_cleanup();
548         return ret;
549     }
550
551     blk_mig_reset_dirty_cursor();
552
553     /* control the rate of transfer */
554     while ((block_mig_state.submitted +
555             block_mig_state.read_done) * BLOCK_SIZE <
556            qemu_file_get_rate_limit(f)) {
557         if (block_mig_state.bulk_completed == 0) {
558             /* first finish the bulk phase */
559             if (blk_mig_save_bulked_block(f) == 0) {
560                 /* finished saving bulk on all devices */
561                 block_mig_state.bulk_completed = 1;
562             }
563         } else {
564             ret = blk_mig_save_dirty_block(f, 1);
565             if (ret != 0) {
566                 /* no more dirty blocks */
567                 break;
568             }
569         }
570     }
571     if (ret) {
572         blk_mig_cleanup();
573         return ret;
574     }
575
576     ret = flush_blks(f);
577     if (ret) {
578         blk_mig_cleanup();
579         return ret;
580     }
581
582     qemu_put_be64(f, BLK_MIG_FLAG_EOS);
583
584     return 0;
585 }
586
587 static int block_save_complete(QEMUFile *f, void *opaque)
588 {
589     int ret;
590
591     DPRINTF("Enter save live complete submitted %d transferred %d\n",
592             block_mig_state.submitted, block_mig_state.transferred);
593
594     ret = flush_blks(f);
595     if (ret) {
596         blk_mig_cleanup();
597         return ret;
598     }
599
600     blk_mig_reset_dirty_cursor();
601
602     /* we know for sure that save bulk is completed and
603        all async read completed */
604     assert(block_mig_state.submitted == 0);
605
606     do {
607         ret = blk_mig_save_dirty_block(f, 0);
608     } while (ret == 0);
609
610     blk_mig_cleanup();
611     if (ret) {
612         return ret;
613     }
614     /* report completion */
615     qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
616
617     DPRINTF("Block migration completed\n");
618
619     qemu_put_be64(f, BLK_MIG_FLAG_EOS);
620
621     return 0;
622 }
623
624 static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
625 {
626
627     DPRINTF("Enter save live pending  %ld\n", get_remaining_dirty());
628
629     return get_remaining_dirty();
630 }
631
632 static int block_load(QEMUFile *f, void *opaque, int version_id)
633 {
634     static int banner_printed;
635     int len, flags;
636     char device_name[256];
637     int64_t addr;
638     BlockDriverState *bs, *bs_prev = NULL;
639     uint8_t *buf;
640     int64_t total_sectors = 0;
641     int nr_sectors;
642     int ret;
643
644     do {
645         addr = qemu_get_be64(f);
646
647         flags = addr & ~BDRV_SECTOR_MASK;
648         addr >>= BDRV_SECTOR_BITS;
649
650         if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) {
651             /* get device name */
652             len = qemu_get_byte(f);
653             qemu_get_buffer(f, (uint8_t *)device_name, len);
654             device_name[len] = '\0';
655
656             bs = bdrv_find(device_name);
657             if (!bs) {
658                 fprintf(stderr, "Error unknown block device %s\n",
659                         device_name);
660                 return -EINVAL;
661             }
662
663             if (bs != bs_prev) {
664                 bs_prev = bs;
665                 total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
666                 if (total_sectors <= 0) {
667                     error_report("Error getting length of block device %s",
668                                  device_name);
669                     return -EINVAL;
670                 }
671             }
672
673             if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) {
674                 nr_sectors = total_sectors - addr;
675             } else {
676                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
677             }
678
679             buf = g_malloc(BLOCK_SIZE);
680
681             qemu_get_buffer(f, buf, BLOCK_SIZE);
682             ret = bdrv_write(bs, addr, buf, nr_sectors);
683
684             g_free(buf);
685             if (ret < 0) {
686                 return ret;
687             }
688         } else if (flags & BLK_MIG_FLAG_PROGRESS) {
689             if (!banner_printed) {
690                 printf("Receiving block device images\n");
691                 banner_printed = 1;
692             }
693             printf("Completed %d %%%c", (int)addr,
694                    (addr == 100) ? '\n' : '\r');
695             fflush(stdout);
696         } else if (!(flags & BLK_MIG_FLAG_EOS)) {
697             fprintf(stderr, "Unknown flags\n");
698             return -EINVAL;
699         }
700         ret = qemu_file_get_error(f);
701         if (ret != 0) {
702             return ret;
703         }
704     } while (!(flags & BLK_MIG_FLAG_EOS));
705
706     return 0;
707 }
708
709 static void block_set_params(const MigrationParams *params, void *opaque)
710 {
711     block_mig_state.blk_enable = params->blk;
712     block_mig_state.shared_base = params->shared;
713
714     /* shared base means that blk_enable = 1 */
715     block_mig_state.blk_enable |= params->shared;
716 }
717
718 static bool block_is_active(void *opaque)
719 {
720     return block_mig_state.blk_enable == 1;
721 }
722
723 SaveVMHandlers savevm_block_handlers = {
724     .set_params = block_set_params,
725     .save_live_setup = block_save_setup,
726     .save_live_iterate = block_save_iterate,
727     .save_live_complete = block_save_complete,
728     .save_live_pending = block_save_pending,
729     .load_state = block_load,
730     .cancel = block_migration_cancel,
731     .is_active = block_is_active,
732 };
733
734 void blk_mig_init(void)
735 {
736     QSIMPLEQ_INIT(&block_mig_state.bmds_list);
737     QSIMPLEQ_INIT(&block_mig_state.blk_list);
738
739     register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers,
740                          &block_mig_state);
741 }
This page took 0.066089 seconds and 4 git commands to generate.