mirror of https://gitee.com/openkylin/qemu.git
Merge remote-tracking branch 'quintela/migration-next-20121017' into staging
* quintela/migration-next-20121017: (41 commits) cpus: create qemu_in_vcpu_thread() savevm: make qemu_file_put_notify() return errors savevm: un-export qemu_file_set_error() block-migration: handle errors with the return codes correctly block-migration: Switch meaning of return value block-migration: make flush_blks() return errors buffered_file: buffered_put_buffer() don't need to set last_error savevm: Only qemu_fflush() can generate errors savevm: make qemu_fill_buffer() be consistent savevm: unexport qemu_ftell() savevm: unfold qemu_fclose_internal() savevm: make qemu_fflush() return an error code savevm: Remove qemu_fseek() virtio-net: use qemu_get_buffer() in a temp buffer savevm: unexport qemu_fflush migration: make migrate_fd_wait_for_unfreeze() return errors buffered_file: make buffered_flush return the error code buffered_file: callers of buffered_flush() already check for errors buffered_file: We can access directly to bandwidth_limit buffered_file: unfold migrate_fd_close ...
This commit is contained in:
commit
f526f3c315
141
arch_init.c
141
arch_init.c
|
@ -31,6 +31,8 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
#include "sysemu.h"
|
#include "sysemu.h"
|
||||||
|
#include "bitops.h"
|
||||||
|
#include "bitmap.h"
|
||||||
#include "arch_init.h"
|
#include "arch_init.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "hw/pc.h"
|
#include "hw/pc.h"
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
#include "hw/pcspk.h"
|
#include "hw/pcspk.h"
|
||||||
#include "qemu/page_cache.h"
|
#include "qemu/page_cache.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
#ifdef DEBUG_ARCH_INIT
|
#ifdef DEBUG_ARCH_INIT
|
||||||
#define DPRINTF(fmt, ...) \
|
#define DPRINTF(fmt, ...) \
|
||||||
|
@ -330,6 +333,78 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
||||||
|
|
||||||
static RAMBlock *last_block;
|
static RAMBlock *last_block;
|
||||||
static ram_addr_t last_offset;
|
static ram_addr_t last_offset;
|
||||||
|
static unsigned long *migration_bitmap;
|
||||||
|
static uint64_t migration_dirty_pages;
|
||||||
|
|
||||||
|
static inline bool migration_bitmap_test_and_reset_dirty(MemoryRegion *mr,
|
||||||
|
ram_addr_t offset)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||||
|
|
||||||
|
ret = test_and_clear_bit(nr, migration_bitmap);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
migration_dirty_pages--;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
||||||
|
ram_addr_t offset)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||||
|
|
||||||
|
ret = test_and_set_bit(nr, migration_bitmap);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
migration_dirty_pages++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void migration_bitmap_sync(void)
|
||||||
|
{
|
||||||
|
RAMBlock *block;
|
||||||
|
ram_addr_t addr;
|
||||||
|
uint64_t num_dirty_pages_init = migration_dirty_pages;
|
||||||
|
MigrationState *s = migrate_get_current();
|
||||||
|
static int64_t start_time;
|
||||||
|
static int64_t num_dirty_pages_period;
|
||||||
|
int64_t end_time;
|
||||||
|
|
||||||
|
if (!start_time) {
|
||||||
|
start_time = qemu_get_clock_ms(rt_clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_migration_bitmap_sync_start();
|
||||||
|
memory_global_sync_dirty_bitmap(get_system_memory());
|
||||||
|
|
||||||
|
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||||
|
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
||||||
|
if (memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
|
||||||
|
DIRTY_MEMORY_MIGRATION)) {
|
||||||
|
migration_bitmap_set_dirty(block->mr, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memory_region_reset_dirty(block->mr, 0, block->length,
|
||||||
|
DIRTY_MEMORY_MIGRATION);
|
||||||
|
}
|
||||||
|
trace_migration_bitmap_sync_end(migration_dirty_pages
|
||||||
|
- num_dirty_pages_init);
|
||||||
|
num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init;
|
||||||
|
end_time = qemu_get_clock_ms(rt_clock);
|
||||||
|
|
||||||
|
/* more than 1 second = 1000 millisecons */
|
||||||
|
if (end_time > start_time + 1000) {
|
||||||
|
s->dirty_pages_rate = num_dirty_pages_period * 1000
|
||||||
|
/ (end_time - start_time);
|
||||||
|
start_time = end_time;
|
||||||
|
num_dirty_pages_period = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ram_save_block: Writes a page of memory to the stream f
|
* ram_save_block: Writes a page of memory to the stream f
|
||||||
|
@ -352,14 +427,10 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
mr = block->mr;
|
mr = block->mr;
|
||||||
if (memory_region_get_dirty(mr, offset, TARGET_PAGE_SIZE,
|
if (migration_bitmap_test_and_reset_dirty(mr, offset)) {
|
||||||
DIRTY_MEMORY_MIGRATION)) {
|
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
|
int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
|
||||||
|
|
||||||
memory_region_reset_dirty(mr, offset, TARGET_PAGE_SIZE,
|
|
||||||
DIRTY_MEMORY_MIGRATION);
|
|
||||||
|
|
||||||
p = memory_region_get_ram_ptr(mr) + offset;
|
p = memory_region_get_ram_ptr(mr) + offset;
|
||||||
|
|
||||||
if (is_dup_page(p)) {
|
if (is_dup_page(p)) {
|
||||||
|
@ -409,7 +480,7 @@ static uint64_t bytes_transferred;
|
||||||
|
|
||||||
static ram_addr_t ram_save_remaining(void)
|
static ram_addr_t ram_save_remaining(void)
|
||||||
{
|
{
|
||||||
return ram_list.dirty_pages;
|
return migration_dirty_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ram_bytes_remaining(void)
|
uint64_t ram_bytes_remaining(void)
|
||||||
|
@ -481,17 +552,27 @@ static void ram_migration_cancel(void *opaque)
|
||||||
migration_end();
|
migration_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reset_ram_globals(void)
|
||||||
|
{
|
||||||
|
last_block = NULL;
|
||||||
|
last_offset = 0;
|
||||||
|
sort_ram_list();
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_WAIT 50 /* ms, half buffered_file limit */
|
#define MAX_WAIT 50 /* ms, half buffered_file limit */
|
||||||
|
|
||||||
static int ram_save_setup(QEMUFile *f, void *opaque)
|
static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||||
{
|
{
|
||||||
ram_addr_t addr;
|
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
|
int64_t ram_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||||
|
|
||||||
|
migration_bitmap = bitmap_new(ram_pages);
|
||||||
|
bitmap_set(migration_bitmap, 1, ram_pages);
|
||||||
|
migration_dirty_pages = ram_pages;
|
||||||
|
|
||||||
bytes_transferred = 0;
|
bytes_transferred = 0;
|
||||||
last_block = NULL;
|
reset_ram_globals();
|
||||||
last_offset = 0;
|
|
||||||
sort_ram_list();
|
|
||||||
|
|
||||||
if (migrate_use_xbzrle()) {
|
if (migrate_use_xbzrle()) {
|
||||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||||
|
@ -506,17 +587,8 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||||
acct_clear();
|
acct_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure all dirty bits are set */
|
|
||||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
|
||||||
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
|
||||||
if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
|
|
||||||
DIRTY_MEMORY_MIGRATION)) {
|
|
||||||
memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_global_dirty_log_start();
|
memory_global_dirty_log_start();
|
||||||
|
migration_bitmap_sync();
|
||||||
|
|
||||||
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
||||||
|
|
||||||
|
@ -537,7 +609,8 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||||
double bwidth = 0;
|
double bwidth = 0;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
uint64_t expected_time;
|
uint64_t expected_downtime;
|
||||||
|
MigrationState *s = migrate_get_current();
|
||||||
|
|
||||||
bytes_transferred_last = bytes_transferred;
|
bytes_transferred_last = bytes_transferred;
|
||||||
bwidth = qemu_get_clock_ns(rt_clock);
|
bwidth = qemu_get_clock_ns(rt_clock);
|
||||||
|
@ -576,31 +649,32 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||||
bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
|
bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
|
||||||
bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
|
bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
|
||||||
|
|
||||||
/* if we haven't transferred anything this round, force expected_time to a
|
/* if we haven't transferred anything this round, force
|
||||||
* a very high value, but without crashing */
|
* expected_downtime to a very high value, but without
|
||||||
|
* crashing */
|
||||||
if (bwidth == 0) {
|
if (bwidth == 0) {
|
||||||
bwidth = 0.000001;
|
bwidth = 0.000001;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||||
|
|
||||||
expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
expected_downtime = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
||||||
|
DPRINTF("ram_save_live: expected(%" PRIu64 ") <= max(" PRIu64 ")?\n",
|
||||||
|
expected_downtime, migrate_max_downtime());
|
||||||
|
|
||||||
DPRINTF("ram_save_live: expected(%" PRIu64 ") <= max(%" PRIu64 ")?\n",
|
if (expected_downtime <= migrate_max_downtime()) {
|
||||||
expected_time, migrate_max_downtime());
|
migration_bitmap_sync();
|
||||||
|
expected_downtime = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
||||||
|
s->expected_downtime = expected_downtime / 1000000; /* ns -> ms */
|
||||||
|
|
||||||
if (expected_time <= migrate_max_downtime()) {
|
return expected_downtime <= migrate_max_downtime();
|
||||||
memory_global_sync_dirty_bitmap(get_system_memory());
|
|
||||||
expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
|
|
||||||
|
|
||||||
return expected_time <= migrate_max_downtime();
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ram_save_complete(QEMUFile *f, void *opaque)
|
static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
{
|
{
|
||||||
memory_global_sync_dirty_bitmap(get_system_memory());
|
migration_bitmap_sync();
|
||||||
|
|
||||||
/* try transferring iterative blocks of memory */
|
/* try transferring iterative blocks of memory */
|
||||||
|
|
||||||
|
@ -619,6 +693,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
|
|
||||||
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
|
||||||
|
|
||||||
|
g_free(migration_bitmap);
|
||||||
|
migration_bitmap = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,20 +423,23 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||||
|
|
||||||
error:
|
error:
|
||||||
DPRINTF("Error reading sector %" PRId64 "\n", sector);
|
DPRINTF("Error reading sector %" PRId64 "\n", sector);
|
||||||
qemu_file_set_error(f, ret);
|
|
||||||
g_free(blk->buf);
|
g_free(blk->buf);
|
||||||
g_free(blk);
|
g_free(blk);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* return value:
|
||||||
|
* 0: too much data for max_downtime
|
||||||
|
* 1: few enough data for max_downtime
|
||||||
|
*/
|
||||||
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||||
{
|
{
|
||||||
BlkMigDevState *bmds;
|
BlkMigDevState *bmds;
|
||||||
int ret = 0;
|
int ret = 1;
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||||
if (mig_save_device_dirty(f, bmds, is_async) == 0) {
|
ret = mig_save_device_dirty(f, bmds, is_async);
|
||||||
ret = 1;
|
if (ret <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,9 +447,10 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_blks(QEMUFile* f)
|
static int flush_blks(QEMUFile *f)
|
||||||
{
|
{
|
||||||
BlkMigBlock *blk;
|
BlkMigBlock *blk;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
|
DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
|
||||||
__FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
|
__FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
|
||||||
|
@ -457,7 +461,7 @@ static void flush_blks(QEMUFile* f)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (blk->ret < 0) {
|
if (blk->ret < 0) {
|
||||||
qemu_file_set_error(f, blk->ret);
|
ret = blk->ret;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
blk_send(f, blk);
|
blk_send(f, blk);
|
||||||
|
@ -474,6 +478,7 @@ static void flush_blks(QEMUFile* f)
|
||||||
DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
|
DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
|
||||||
block_mig_state.submitted, block_mig_state.read_done,
|
block_mig_state.submitted, block_mig_state.read_done,
|
||||||
block_mig_state.transferred);
|
block_mig_state.transferred);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t get_remaining_dirty(void)
|
static int64_t get_remaining_dirty(void)
|
||||||
|
@ -555,9 +560,7 @@ static int block_save_setup(QEMUFile *f, void *opaque)
|
||||||
/* start track dirty blocks */
|
/* start track dirty blocks */
|
||||||
set_dirty_tracking(1);
|
set_dirty_tracking(1);
|
||||||
|
|
||||||
flush_blks(f);
|
ret = flush_blks(f);
|
||||||
|
|
||||||
ret = qemu_file_get_error(f);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
blk_mig_cleanup();
|
blk_mig_cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -577,9 +580,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||||
DPRINTF("Enter save live iterate submitted %d transferred %d\n",
|
DPRINTF("Enter save live iterate submitted %d transferred %d\n",
|
||||||
block_mig_state.submitted, block_mig_state.transferred);
|
block_mig_state.submitted, block_mig_state.transferred);
|
||||||
|
|
||||||
flush_blks(f);
|
ret = flush_blks(f);
|
||||||
|
|
||||||
ret = qemu_file_get_error(f);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
blk_mig_cleanup();
|
blk_mig_cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -598,16 +599,19 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
|
||||||
block_mig_state.bulk_completed = 1;
|
block_mig_state.bulk_completed = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (blk_mig_save_dirty_block(f, 1) == 0) {
|
ret = blk_mig_save_dirty_block(f, 1);
|
||||||
|
if (ret != 0) {
|
||||||
/* no more dirty blocks */
|
/* no more dirty blocks */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ret) {
|
||||||
|
blk_mig_cleanup();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
flush_blks(f);
|
ret = flush_blks(f);
|
||||||
|
|
||||||
ret = qemu_file_get_error(f);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
blk_mig_cleanup();
|
blk_mig_cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -625,9 +629,7 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
||||||
DPRINTF("Enter save live complete submitted %d transferred %d\n",
|
DPRINTF("Enter save live complete submitted %d transferred %d\n",
|
||||||
block_mig_state.submitted, block_mig_state.transferred);
|
block_mig_state.submitted, block_mig_state.transferred);
|
||||||
|
|
||||||
flush_blks(f);
|
ret = flush_blks(f);
|
||||||
|
|
||||||
ret = qemu_file_get_error(f);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
blk_mig_cleanup();
|
blk_mig_cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -639,18 +641,16 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
||||||
all async read completed */
|
all async read completed */
|
||||||
assert(block_mig_state.submitted == 0);
|
assert(block_mig_state.submitted == 0);
|
||||||
|
|
||||||
while (blk_mig_save_dirty_block(f, 0) != 0) {
|
do {
|
||||||
/* Do nothing */
|
ret = blk_mig_save_dirty_block(f, 0);
|
||||||
}
|
} while (ret == 0);
|
||||||
|
|
||||||
blk_mig_cleanup();
|
blk_mig_cleanup();
|
||||||
|
|
||||||
/* report completion */
|
|
||||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
|
||||||
|
|
||||||
ret = qemu_file_get_error(f);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/* report completion */
|
||||||
|
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||||
|
|
||||||
DPRINTF("Block migration completed\n");
|
DPRINTF("Block migration completed\n");
|
||||||
|
|
||||||
|
|
129
buffered_file.c
129
buffered_file.c
|
@ -23,11 +23,7 @@
|
||||||
|
|
||||||
typedef struct QEMUFileBuffered
|
typedef struct QEMUFileBuffered
|
||||||
{
|
{
|
||||||
BufferedPutFunc *put_buffer;
|
MigrationState *migration_state;
|
||||||
BufferedPutReadyFunc *put_ready;
|
|
||||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
|
|
||||||
BufferedCloseFunc *close;
|
|
||||||
void *opaque;
|
|
||||||
QEMUFile *file;
|
QEMUFile *file;
|
||||||
int freeze_output;
|
int freeze_output;
|
||||||
size_t bytes_xfer;
|
size_t bytes_xfer;
|
||||||
|
@ -50,70 +46,60 @@ static void buffered_append(QEMUFileBuffered *s,
|
||||||
const uint8_t *buf, size_t size)
|
const uint8_t *buf, size_t size)
|
||||||
{
|
{
|
||||||
if (size > (s->buffer_capacity - s->buffer_size)) {
|
if (size > (s->buffer_capacity - s->buffer_size)) {
|
||||||
void *tmp;
|
|
||||||
|
|
||||||
DPRINTF("increasing buffer capacity from %zu by %zu\n",
|
DPRINTF("increasing buffer capacity from %zu by %zu\n",
|
||||||
s->buffer_capacity, size + 1024);
|
s->buffer_capacity, size + 1024);
|
||||||
|
|
||||||
s->buffer_capacity += size + 1024;
|
s->buffer_capacity += size + 1024;
|
||||||
|
|
||||||
tmp = g_realloc(s->buffer, s->buffer_capacity);
|
s->buffer = g_realloc(s->buffer, s->buffer_capacity);
|
||||||
if (tmp == NULL) {
|
|
||||||
fprintf(stderr, "qemu file buffer expansion failed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->buffer = tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(s->buffer + s->buffer_size, buf, size);
|
memcpy(s->buffer + s->buffer_size, buf, size);
|
||||||
s->buffer_size += size;
|
s->buffer_size += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buffered_flush(QEMUFileBuffered *s)
|
static ssize_t buffered_flush(QEMUFileBuffered *s)
|
||||||
{
|
{
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
int error;
|
ssize_t ret = 0;
|
||||||
|
|
||||||
error = qemu_file_get_error(s->file);
|
|
||||||
if (error != 0) {
|
|
||||||
DPRINTF("flush when error, bailing: %s\n", strerror(-error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
|
DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
|
||||||
|
|
||||||
while (offset < s->buffer_size) {
|
while (s->bytes_xfer < s->xfer_limit && offset < s->buffer_size) {
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
ret = s->put_buffer(s->opaque, s->buffer + offset,
|
ret = migrate_fd_put_buffer(s->migration_state, s->buffer + offset,
|
||||||
s->buffer_size - offset);
|
s->buffer_size - offset);
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
DPRINTF("backend not ready, freezing\n");
|
DPRINTF("backend not ready, freezing\n");
|
||||||
|
ret = 0;
|
||||||
s->freeze_output = 1;
|
s->freeze_output = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
DPRINTF("error flushing data, %zd\n", ret);
|
DPRINTF("error flushing data, %zd\n", ret);
|
||||||
qemu_file_set_error(s->file, ret);
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
DPRINTF("flushed %zd byte(s)\n", ret);
|
DPRINTF("flushed %zd byte(s)\n", ret);
|
||||||
offset += ret;
|
offset += ret;
|
||||||
|
s->bytes_xfer += ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
|
DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
|
||||||
memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
|
memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
|
||||||
s->buffer_size -= offset;
|
s->buffer_size -= offset;
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
|
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
|
||||||
{
|
{
|
||||||
QEMUFileBuffered *s = opaque;
|
QEMUFileBuffered *s = opaque;
|
||||||
int offset = 0, error;
|
ssize_t error;
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
|
DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
|
||||||
|
|
||||||
|
@ -126,65 +112,54 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
|
||||||
DPRINTF("unfreezing output\n");
|
DPRINTF("unfreezing output\n");
|
||||||
s->freeze_output = 0;
|
s->freeze_output = 0;
|
||||||
|
|
||||||
buffered_flush(s);
|
if (size > 0) {
|
||||||
|
DPRINTF("buffering %d bytes\n", size - offset);
|
||||||
while (!s->freeze_output && offset < size) {
|
buffered_append(s, buf, size);
|
||||||
if (s->bytes_xfer > s->xfer_limit) {
|
|
||||||
DPRINTF("transfer limit exceeded when putting\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = s->put_buffer(s->opaque, buf + offset, size - offset);
|
|
||||||
if (ret == -EAGAIN) {
|
|
||||||
DPRINTF("backend not ready, freezing\n");
|
|
||||||
s->freeze_output = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
|
||||||
DPRINTF("error putting\n");
|
|
||||||
qemu_file_set_error(s->file, ret);
|
|
||||||
offset = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF("put %zd byte(s)\n", ret);
|
|
||||||
offset += ret;
|
|
||||||
s->bytes_xfer += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset >= 0) {
|
error = buffered_flush(s);
|
||||||
DPRINTF("buffering %d bytes\n", size - offset);
|
if (error < 0) {
|
||||||
buffered_append(s, buf + offset, size - offset);
|
DPRINTF("buffered flush error. bailing: %s\n", strerror(-error));
|
||||||
offset = size;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == 0 && size == 0) {
|
if (pos == 0 && size == 0) {
|
||||||
DPRINTF("file is ready\n");
|
DPRINTF("file is ready\n");
|
||||||
if (s->bytes_xfer <= s->xfer_limit) {
|
if (!s->freeze_output && s->bytes_xfer < s->xfer_limit) {
|
||||||
DPRINTF("notifying client\n");
|
DPRINTF("notifying client\n");
|
||||||
s->put_ready(s->opaque);
|
migrate_fd_put_ready(s->migration_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int buffered_close(void *opaque)
|
static int buffered_close(void *opaque)
|
||||||
{
|
{
|
||||||
QEMUFileBuffered *s = opaque;
|
QEMUFileBuffered *s = opaque;
|
||||||
int ret;
|
ssize_t ret = 0;
|
||||||
|
int ret2;
|
||||||
|
|
||||||
DPRINTF("closing\n");
|
DPRINTF("closing\n");
|
||||||
|
|
||||||
|
s->xfer_limit = INT_MAX;
|
||||||
while (!qemu_file_get_error(s->file) && s->buffer_size) {
|
while (!qemu_file_get_error(s->file) && s->buffer_size) {
|
||||||
buffered_flush(s);
|
ret = buffered_flush(s);
|
||||||
if (s->freeze_output)
|
if (ret < 0) {
|
||||||
s->wait_for_unfreeze(s->opaque);
|
break;
|
||||||
|
}
|
||||||
|
if (s->freeze_output) {
|
||||||
|
ret = migrate_fd_wait_for_unfreeze(s->migration_state);
|
||||||
|
if (ret < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = s->close(s->opaque);
|
ret2 = migrate_fd_close(s->migration_state);
|
||||||
|
if (ret >= 0) {
|
||||||
|
ret = ret2;
|
||||||
|
}
|
||||||
qemu_del_timer(s->timer);
|
qemu_del_timer(s->timer);
|
||||||
qemu_free_timer(s->timer);
|
qemu_free_timer(s->timer);
|
||||||
g_free(s->buffer);
|
g_free(s->buffer);
|
||||||
|
@ -256,29 +231,17 @@ static void buffered_rate_tick(void *opaque)
|
||||||
|
|
||||||
s->bytes_xfer = 0;
|
s->bytes_xfer = 0;
|
||||||
|
|
||||||
buffered_flush(s);
|
buffered_put_buffer(s, NULL, 0, 0);
|
||||||
|
|
||||||
/* Add some checks around this */
|
|
||||||
s->put_ready(s->opaque);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
|
QEMUFile *qemu_fopen_ops_buffered(MigrationState *migration_state)
|
||||||
size_t bytes_per_sec,
|
|
||||||
BufferedPutFunc *put_buffer,
|
|
||||||
BufferedPutReadyFunc *put_ready,
|
|
||||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
|
|
||||||
BufferedCloseFunc *close)
|
|
||||||
{
|
{
|
||||||
QEMUFileBuffered *s;
|
QEMUFileBuffered *s;
|
||||||
|
|
||||||
s = g_malloc0(sizeof(*s));
|
s = g_malloc0(sizeof(*s));
|
||||||
|
|
||||||
s->opaque = opaque;
|
s->migration_state = migration_state;
|
||||||
s->xfer_limit = bytes_per_sec / 10;
|
s->xfer_limit = migration_state->bandwidth_limit / 10;
|
||||||
s->put_buffer = put_buffer;
|
|
||||||
s->put_ready = put_ready;
|
|
||||||
s->wait_for_unfreeze = wait_for_unfreeze;
|
|
||||||
s->close = close;
|
|
||||||
|
|
||||||
s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
|
s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
|
||||||
buffered_close, buffered_rate_limit,
|
buffered_close, buffered_rate_limit,
|
||||||
|
|
|
@ -15,16 +15,8 @@
|
||||||
#define QEMU_BUFFERED_FILE_H
|
#define QEMU_BUFFERED_FILE_H
|
||||||
|
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
|
#include "migration.h"
|
||||||
|
|
||||||
typedef ssize_t (BufferedPutFunc)(void *opaque, const void *data, size_t size);
|
QEMUFile *qemu_fopen_ops_buffered(MigrationState *migration_state);
|
||||||
typedef void (BufferedPutReadyFunc)(void *opaque);
|
|
||||||
typedef void (BufferedWaitForUnfreezeFunc)(void *opaque);
|
|
||||||
typedef int (BufferedCloseFunc)(void *opaque);
|
|
||||||
|
|
||||||
QEMUFile *qemu_fopen_ops_buffered(void *opaque, size_t xfer_limit,
|
|
||||||
BufferedPutFunc *put_buffer,
|
|
||||||
BufferedPutReadyFunc *put_ready,
|
|
||||||
BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
|
|
||||||
BufferedCloseFunc *close);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -500,7 +500,6 @@ typedef struct RAMBlock {
|
||||||
typedef struct RAMList {
|
typedef struct RAMList {
|
||||||
uint8_t *phys_dirty;
|
uint8_t *phys_dirty;
|
||||||
QLIST_HEAD(, RAMBlock) blocks;
|
QLIST_HEAD(, RAMBlock) blocks;
|
||||||
uint64_t dirty_pages;
|
|
||||||
} RAMList;
|
} RAMList;
|
||||||
extern RAMList ram_list;
|
extern RAMList ram_list;
|
||||||
|
|
||||||
|
@ -518,6 +517,7 @@ extern int mem_prealloc;
|
||||||
#define TLB_MMIO (1 << 5)
|
#define TLB_MMIO (1 << 5)
|
||||||
|
|
||||||
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||||
|
ram_addr_t last_ram_offset(void);
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
#endif /* !CONFIG_USER_ONLY */
|
||||||
|
|
||||||
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||||
|
|
9
cpus.c
9
cpus.c
|
@ -898,6 +898,11 @@ int qemu_cpu_is_self(void *_env)
|
||||||
return qemu_thread_is_self(cpu->thread);
|
return qemu_thread_is_self(cpu->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool qemu_in_vcpu_thread(void)
|
||||||
|
{
|
||||||
|
return cpu_single_env && qemu_cpu_is_self(cpu_single_env);
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_mutex_lock_iothread(void)
|
void qemu_mutex_lock_iothread(void)
|
||||||
{
|
{
|
||||||
if (!tcg_enabled()) {
|
if (!tcg_enabled()) {
|
||||||
|
@ -943,7 +948,7 @@ void pause_all_vcpus(void)
|
||||||
penv = penv->next_cpu;
|
penv = penv->next_cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qemu_thread_is_self(&io_thread)) {
|
if (qemu_in_vcpu_thread()) {
|
||||||
cpu_stop_current();
|
cpu_stop_current();
|
||||||
if (!kvm_enabled()) {
|
if (!kvm_enabled()) {
|
||||||
while (penv) {
|
while (penv) {
|
||||||
|
@ -1060,7 +1065,7 @@ void cpu_stop_current(void)
|
||||||
|
|
||||||
void vm_stop(RunState state)
|
void vm_stop(RunState state)
|
||||||
{
|
{
|
||||||
if (!qemu_thread_is_self(&io_thread)) {
|
if (qemu_in_vcpu_thread()) {
|
||||||
qemu_system_vmstop_request(state);
|
qemu_system_vmstop_request(state);
|
||||||
/*
|
/*
|
||||||
* FIXME: should not return to device code in case
|
* FIXME: should not return to device code in case
|
||||||
|
|
2
exec.c
2
exec.c
|
@ -2449,7 +2449,7 @@ static ram_addr_t find_ram_offset(ram_addr_t size)
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ram_addr_t last_ram_offset(void)
|
ram_addr_t last_ram_offset(void)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
ram_addr_t last = 0;
|
ram_addr_t last = 0;
|
||||||
|
|
12
hmp.c
12
hmp.c
|
@ -152,6 +152,14 @@ void hmp_info_migrate(Monitor *mon)
|
||||||
monitor_printf(mon, "Migration status: %s\n", info->status);
|
monitor_printf(mon, "Migration status: %s\n", info->status);
|
||||||
monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
|
monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
|
||||||
info->total_time);
|
info->total_time);
|
||||||
|
if (info->has_expected_downtime) {
|
||||||
|
monitor_printf(mon, "expected downtime: %" PRIu64 " milliseconds\n",
|
||||||
|
info->expected_downtime);
|
||||||
|
}
|
||||||
|
if (info->has_downtime) {
|
||||||
|
monitor_printf(mon, "downtime: %" PRIu64 " milliseconds\n",
|
||||||
|
info->downtime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->has_ram) {
|
if (info->has_ram) {
|
||||||
|
@ -167,6 +175,10 @@ void hmp_info_migrate(Monitor *mon)
|
||||||
info->ram->normal);
|
info->ram->normal);
|
||||||
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
|
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
|
||||||
info->ram->normal_bytes >> 10);
|
info->ram->normal_bytes >> 10);
|
||||||
|
if (info->ram->dirty_pages_rate) {
|
||||||
|
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
|
||||||
|
info->ram->dirty_pages_rate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->has_disk) {
|
if (info->has_disk) {
|
||||||
|
|
|
@ -921,7 +921,9 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
qemu_get_buffer(f, n->mac_table.macs,
|
qemu_get_buffer(f, n->mac_table.macs,
|
||||||
n->mac_table.in_use * ETH_ALEN);
|
n->mac_table.in_use * ETH_ALEN);
|
||||||
} else if (n->mac_table.in_use) {
|
} else if (n->mac_table.in_use) {
|
||||||
qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR);
|
uint8_t *buf = g_malloc0(n->mac_table.in_use);
|
||||||
|
qemu_get_buffer(f, buf, n->mac_table.in_use * ETH_ALEN);
|
||||||
|
g_free(buf);
|
||||||
n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1;
|
n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1;
|
||||||
n->mac_table.in_use = 0;
|
n->mac_table.in_use = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,11 +90,6 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start,
|
||||||
static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
|
static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
|
||||||
int dirty_flags)
|
int dirty_flags)
|
||||||
{
|
{
|
||||||
if ((dirty_flags & MIGRATION_DIRTY_FLAG) &&
|
|
||||||
!cpu_physical_memory_get_dirty(addr, TARGET_PAGE_SIZE,
|
|
||||||
MIGRATION_DIRTY_FLAG)) {
|
|
||||||
ram_list.dirty_pages++;
|
|
||||||
}
|
|
||||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
|
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +103,6 @@ static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr,
|
||||||
{
|
{
|
||||||
int mask = ~dirty_flags;
|
int mask = ~dirty_flags;
|
||||||
|
|
||||||
if ((dirty_flags & MIGRATION_DIRTY_FLAG) &&
|
|
||||||
cpu_physical_memory_get_dirty(addr, TARGET_PAGE_SIZE,
|
|
||||||
MIGRATION_DIRTY_FLAG)) {
|
|
||||||
ram_list.dirty_pages--;
|
|
||||||
}
|
|
||||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask;
|
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
46
migration.c
46
migration.c
|
@ -53,7 +53,7 @@ static NotifierList migration_state_notifiers =
|
||||||
migrations at once. For now we don't need to add
|
migrations at once. For now we don't need to add
|
||||||
dynamic creation of migration */
|
dynamic creation of migration */
|
||||||
|
|
||||||
static MigrationState *migrate_get_current(void)
|
MigrationState *migrate_get_current(void)
|
||||||
{
|
{
|
||||||
static MigrationState current_migration = {
|
static MigrationState current_migration = {
|
||||||
.state = MIG_STATE_SETUP,
|
.state = MIG_STATE_SETUP,
|
||||||
|
@ -169,6 +169,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
||||||
info->has_total_time = true;
|
info->has_total_time = true;
|
||||||
info->total_time = qemu_get_clock_ms(rt_clock)
|
info->total_time = qemu_get_clock_ms(rt_clock)
|
||||||
- s->total_time;
|
- s->total_time;
|
||||||
|
info->has_expected_downtime = true;
|
||||||
|
info->expected_downtime = s->expected_downtime;
|
||||||
|
|
||||||
info->has_ram = true;
|
info->has_ram = true;
|
||||||
info->ram = g_malloc0(sizeof(*info->ram));
|
info->ram = g_malloc0(sizeof(*info->ram));
|
||||||
|
@ -178,6 +180,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
||||||
info->ram->duplicate = dup_mig_pages_transferred();
|
info->ram->duplicate = dup_mig_pages_transferred();
|
||||||
info->ram->normal = norm_mig_pages_transferred();
|
info->ram->normal = norm_mig_pages_transferred();
|
||||||
info->ram->normal_bytes = norm_mig_bytes_transferred();
|
info->ram->normal_bytes = norm_mig_bytes_transferred();
|
||||||
|
info->ram->dirty_pages_rate = s->dirty_pages_rate;
|
||||||
|
|
||||||
|
|
||||||
if (blk_mig_active()) {
|
if (blk_mig_active()) {
|
||||||
info->has_disk = true;
|
info->has_disk = true;
|
||||||
|
@ -195,6 +199,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
||||||
info->has_status = true;
|
info->has_status = true;
|
||||||
info->status = g_strdup("completed");
|
info->status = g_strdup("completed");
|
||||||
info->total_time = s->total_time;
|
info->total_time = s->total_time;
|
||||||
|
info->has_downtime = true;
|
||||||
|
info->downtime = s->downtime;
|
||||||
|
|
||||||
info->has_ram = true;
|
info->has_ram = true;
|
||||||
info->ram = g_malloc0(sizeof(*info->ram));
|
info->ram = g_malloc0(sizeof(*info->ram));
|
||||||
|
@ -281,18 +287,18 @@ static void migrate_fd_completed(MigrationState *s)
|
||||||
static void migrate_fd_put_notify(void *opaque)
|
static void migrate_fd_put_notify(void *opaque)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
MigrationState *s = opaque;
|
||||||
|
int ret;
|
||||||
|
|
||||||
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
|
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
|
||||||
qemu_file_put_notify(s->file);
|
ret = qemu_file_put_notify(s->file);
|
||||||
if (s->file && qemu_file_get_error(s->file)) {
|
if (ret) {
|
||||||
migrate_fd_error(s);
|
migrate_fd_error(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t migrate_fd_put_buffer(void *opaque, const void *data,
|
ssize_t migrate_fd_put_buffer(MigrationState *s, const void *data,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
if (s->state != MIG_STATE_ACTIVE) {
|
if (s->state != MIG_STATE_ACTIVE) {
|
||||||
|
@ -313,9 +319,8 @@ static ssize_t migrate_fd_put_buffer(void *opaque, const void *data,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void migrate_fd_put_ready(void *opaque)
|
void migrate_fd_put_ready(MigrationState *s)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (s->state != MIG_STATE_ACTIVE) {
|
if (s->state != MIG_STATE_ACTIVE) {
|
||||||
|
@ -329,8 +334,10 @@ static void migrate_fd_put_ready(void *opaque)
|
||||||
migrate_fd_error(s);
|
migrate_fd_error(s);
|
||||||
} else if (ret == 1) {
|
} else if (ret == 1) {
|
||||||
int old_vm_running = runstate_is_running();
|
int old_vm_running = runstate_is_running();
|
||||||
|
int64_t start_time, end_time;
|
||||||
|
|
||||||
DPRINTF("done iterating\n");
|
DPRINTF("done iterating\n");
|
||||||
|
start_time = qemu_get_clock_ms(rt_clock);
|
||||||
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
||||||
vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||||
|
|
||||||
|
@ -339,7 +346,9 @@ static void migrate_fd_put_ready(void *opaque)
|
||||||
} else {
|
} else {
|
||||||
migrate_fd_completed(s);
|
migrate_fd_completed(s);
|
||||||
}
|
}
|
||||||
s->total_time = qemu_get_clock_ms(rt_clock) - s->total_time;
|
end_time = qemu_get_clock_ms(rt_clock);
|
||||||
|
s->total_time = end_time - s->total_time;
|
||||||
|
s->downtime = end_time - start_time;
|
||||||
if (s->state != MIG_STATE_COMPLETED) {
|
if (s->state != MIG_STATE_COMPLETED) {
|
||||||
if (old_vm_running) {
|
if (old_vm_running) {
|
||||||
vm_start();
|
vm_start();
|
||||||
|
@ -362,14 +371,13 @@ static void migrate_fd_cancel(MigrationState *s)
|
||||||
migrate_fd_cleanup(s);
|
migrate_fd_cleanup(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void migrate_fd_wait_for_unfreeze(void *opaque)
|
int migrate_fd_wait_for_unfreeze(MigrationState *s)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DPRINTF("wait for unfreeze\n");
|
DPRINTF("wait for unfreeze\n");
|
||||||
if (s->state != MIG_STATE_ACTIVE)
|
if (s->state != MIG_STATE_ACTIVE)
|
||||||
return;
|
return -EINVAL;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
fd_set wfds;
|
fd_set wfds;
|
||||||
|
@ -381,14 +389,13 @@ static void migrate_fd_wait_for_unfreeze(void *opaque)
|
||||||
} while (ret == -1 && (s->get_error(s)) == EINTR);
|
} while (ret == -1 && (s->get_error(s)) == EINTR);
|
||||||
|
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
qemu_file_set_error(s->file, -s->get_error(s));
|
return -s->get_error(s);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int migrate_fd_close(void *opaque)
|
int migrate_fd_close(MigrationState *s)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
|
||||||
|
|
||||||
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
|
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
|
||||||
return s->close(s);
|
return s->close(s);
|
||||||
}
|
}
|
||||||
|
@ -424,12 +431,7 @@ void migrate_fd_connect(MigrationState *s)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
s->state = MIG_STATE_ACTIVE;
|
s->state = MIG_STATE_ACTIVE;
|
||||||
s->file = qemu_fopen_ops_buffered(s,
|
s->file = qemu_fopen_ops_buffered(s);
|
||||||
s->bandwidth_limit,
|
|
||||||
migrate_fd_put_buffer,
|
|
||||||
migrate_fd_put_ready,
|
|
||||||
migrate_fd_wait_for_unfreeze,
|
|
||||||
migrate_fd_close);
|
|
||||||
|
|
||||||
DPRINTF("beginning savevm\n");
|
DPRINTF("beginning savevm\n");
|
||||||
ret = qemu_savevm_state_begin(s->file, &s->params);
|
ret = qemu_savevm_state_begin(s->file, &s->params);
|
||||||
|
|
10
migration.h
10
migration.h
|
@ -40,6 +40,9 @@ struct MigrationState
|
||||||
void *opaque;
|
void *opaque;
|
||||||
MigrationParams params;
|
MigrationParams params;
|
||||||
int64_t total_time;
|
int64_t total_time;
|
||||||
|
int64_t downtime;
|
||||||
|
int64_t expected_downtime;
|
||||||
|
int64_t dirty_pages_rate;
|
||||||
bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
|
bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
|
||||||
int64_t xbzrle_cache_size;
|
int64_t xbzrle_cache_size;
|
||||||
};
|
};
|
||||||
|
@ -75,11 +78,18 @@ void migrate_fd_error(MigrationState *s);
|
||||||
|
|
||||||
void migrate_fd_connect(MigrationState *s);
|
void migrate_fd_connect(MigrationState *s);
|
||||||
|
|
||||||
|
ssize_t migrate_fd_put_buffer(MigrationState *s, const void *data,
|
||||||
|
size_t size);
|
||||||
|
void migrate_fd_put_ready(MigrationState *s);
|
||||||
|
int migrate_fd_wait_for_unfreeze(MigrationState *s);
|
||||||
|
int migrate_fd_close(MigrationState *s);
|
||||||
|
|
||||||
void add_migration_state_change_notifier(Notifier *notify);
|
void add_migration_state_change_notifier(Notifier *notify);
|
||||||
void remove_migration_state_change_notifier(Notifier *notify);
|
void remove_migration_state_change_notifier(Notifier *notify);
|
||||||
bool migration_is_active(MigrationState *);
|
bool migration_is_active(MigrationState *);
|
||||||
bool migration_has_finished(MigrationState *);
|
bool migration_has_finished(MigrationState *);
|
||||||
bool migration_has_failed(MigrationState *);
|
bool migration_has_failed(MigrationState *);
|
||||||
|
MigrationState *migrate_get_current(void);
|
||||||
|
|
||||||
uint64_t ram_bytes_remaining(void);
|
uint64_t ram_bytes_remaining(void);
|
||||||
uint64_t ram_bytes_transferred(void);
|
uint64_t ram_bytes_transferred(void);
|
||||||
|
|
|
@ -383,13 +383,17 @@
|
||||||
#
|
#
|
||||||
# @normal : number of normal pages (since 1.2)
|
# @normal : number of normal pages (since 1.2)
|
||||||
#
|
#
|
||||||
# @normal-bytes : number of normal bytes sent (since 1.2)
|
# @normal-bytes: number of normal bytes sent (since 1.2)
|
||||||
|
#
|
||||||
|
# @dirty-pages-rate: number of pages dirtied by second by the
|
||||||
|
# guest (since 1.3)
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
{ 'type': 'MigrationStats',
|
{ 'type': 'MigrationStats',
|
||||||
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
|
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
|
||||||
'duplicate': 'int', 'normal': 'int', 'normal-bytes': 'int' } }
|
'duplicate': 'int', 'normal': 'int', 'normal-bytes': 'int',
|
||||||
|
'dirty-pages-rate' : 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @XBZRLECacheStats
|
# @XBZRLECacheStats
|
||||||
|
@ -438,13 +442,23 @@
|
||||||
# If migration has ended, it returns the total migration
|
# If migration has ended, it returns the total migration
|
||||||
# time. (since 1.2)
|
# time. (since 1.2)
|
||||||
#
|
#
|
||||||
|
# @downtime: #optional only present when migration finishes correctly
|
||||||
|
# total downtime in milliseconds for the guest.
|
||||||
|
# (since 1.3)
|
||||||
|
#
|
||||||
|
# @expected-downtime: #optional only present while migration is active
|
||||||
|
# expected downtime in milliseconds for the guest in last walk
|
||||||
|
# of the dirty bitmap. (since 1.3)
|
||||||
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
{ 'type': 'MigrationInfo',
|
{ 'type': 'MigrationInfo',
|
||||||
'data': {'*status': 'str', '*ram': 'MigrationStats',
|
'data': {'*status': 'str', '*ram': 'MigrationStats',
|
||||||
'*disk': 'MigrationStats',
|
'*disk': 'MigrationStats',
|
||||||
'*xbzrle-cache': 'XBZRLECacheStats',
|
'*xbzrle-cache': 'XBZRLECacheStats',
|
||||||
'*total-time': 'int'} }
|
'*total-time': 'int',
|
||||||
|
'*expected-downtime': 'int',
|
||||||
|
'*downtime': 'int'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-migrate
|
# @query-migrate
|
||||||
|
|
|
@ -71,7 +71,6 @@ QEMUFile *qemu_fopen_socket(int fd);
|
||||||
QEMUFile *qemu_popen(FILE *popen_file, const char *mode);
|
QEMUFile *qemu_popen(FILE *popen_file, const char *mode);
|
||||||
QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
|
QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
|
||||||
int qemu_stdio_fd(QEMUFile *f);
|
int qemu_stdio_fd(QEMUFile *f);
|
||||||
void qemu_fflush(QEMUFile *f);
|
|
||||||
int qemu_fclose(QEMUFile *f);
|
int qemu_fclose(QEMUFile *f);
|
||||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
|
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
|
||||||
void qemu_put_byte(QEMUFile *f, int v);
|
void qemu_put_byte(QEMUFile *f, int v);
|
||||||
|
@ -104,12 +103,11 @@ int qemu_file_rate_limit(QEMUFile *f);
|
||||||
int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
|
int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
|
||||||
int64_t qemu_file_get_rate_limit(QEMUFile *f);
|
int64_t qemu_file_get_rate_limit(QEMUFile *f);
|
||||||
int qemu_file_get_error(QEMUFile *f);
|
int qemu_file_get_error(QEMUFile *f);
|
||||||
void qemu_file_set_error(QEMUFile *f, int error);
|
|
||||||
|
|
||||||
/* Try to send any outstanding data. This function is useful when output is
|
/* Try to send any outstanding data. This function is useful when output is
|
||||||
* halted due to rate limiting or EAGAIN errors occur as it can be used to
|
* halted due to rate limiting or EAGAIN errors occur as it can be used to
|
||||||
* resume output. */
|
* resume output. */
|
||||||
void qemu_file_put_notify(QEMUFile *f);
|
int qemu_file_put_notify(QEMUFile *f);
|
||||||
|
|
||||||
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
|
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
|
||||||
{
|
{
|
||||||
|
@ -231,8 +229,4 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv)
|
||||||
{
|
{
|
||||||
qemu_get_be64s(f, (uint64_t *)pv);
|
qemu_get_be64s(f, (uint64_t *)pv);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t qemu_ftell(QEMUFile *f);
|
|
||||||
int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2304,6 +2304,11 @@ The main json-object contains the following:
|
||||||
- "total-time": total amount of ms since migration started. If
|
- "total-time": total amount of ms since migration started. If
|
||||||
migration has ended, it returns the total migration
|
migration has ended, it returns the total migration
|
||||||
time (json-int)
|
time (json-int)
|
||||||
|
- "downtime": only present when migration has finished correctly
|
||||||
|
total amount in ms for downtime that happened (json-int)
|
||||||
|
- "expected-downtime": only present while migration is active
|
||||||
|
total amount in ms for downtime that was calculated on
|
||||||
|
the last bitmap round (json-int)
|
||||||
- "ram": only present if "status" is "active", it is a json-object with the
|
- "ram": only present if "status" is "active", it is a json-object with the
|
||||||
following RAM information (in bytes):
|
following RAM information (in bytes):
|
||||||
- "transferred": amount transferred (json-int)
|
- "transferred": amount transferred (json-int)
|
||||||
|
@ -2341,6 +2346,7 @@ Examples:
|
||||||
"remaining":123,
|
"remaining":123,
|
||||||
"total":246,
|
"total":246,
|
||||||
"total-time":12345,
|
"total-time":12345,
|
||||||
|
"downtime":12345,
|
||||||
"duplicate":123,
|
"duplicate":123,
|
||||||
"normal":123,
|
"normal":123,
|
||||||
"normal-bytes":123456
|
"normal-bytes":123456
|
||||||
|
@ -2364,6 +2370,7 @@ Examples:
|
||||||
"remaining":123,
|
"remaining":123,
|
||||||
"total":246,
|
"total":246,
|
||||||
"total-time":12345,
|
"total-time":12345,
|
||||||
|
"expected-downtime":12345,
|
||||||
"duplicate":123,
|
"duplicate":123,
|
||||||
"normal":123,
|
"normal":123,
|
||||||
"normal-bytes":123456
|
"normal-bytes":123456
|
||||||
|
@ -2382,6 +2389,7 @@ Examples:
|
||||||
"remaining":1053304,
|
"remaining":1053304,
|
||||||
"transferred":3720,
|
"transferred":3720,
|
||||||
"total-time":12345,
|
"total-time":12345,
|
||||||
|
"expected-downtime":12345,
|
||||||
"duplicate":123,
|
"duplicate":123,
|
||||||
"normal":123,
|
"normal":123,
|
||||||
"normal-bytes":123456
|
"normal-bytes":123456
|
||||||
|
@ -2406,6 +2414,7 @@ Examples:
|
||||||
"remaining":1053304,
|
"remaining":1053304,
|
||||||
"transferred":3720,
|
"transferred":3720,
|
||||||
"total-time":12345,
|
"total-time":12345,
|
||||||
|
"expected-downtime":12345,
|
||||||
"duplicate":10,
|
"duplicate":10,
|
||||||
"normal":3333,
|
"normal":3333,
|
||||||
"normal-bytes":3412992
|
"normal-bytes":3412992
|
||||||
|
|
117
savevm.c
117
savevm.c
|
@ -440,42 +440,29 @@ int qemu_file_get_error(QEMUFile *f)
|
||||||
return f->last_error;
|
return f->last_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_file_set_error(QEMUFile *f, int ret)
|
static void qemu_file_set_error(QEMUFile *f, int ret)
|
||||||
{
|
{
|
||||||
f->last_error = ret;
|
f->last_error = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets last_error conditionally
|
|
||||||
*
|
|
||||||
* Sets last_error only if ret is negative _and_ no error
|
|
||||||
* was set before.
|
|
||||||
*/
|
|
||||||
static void qemu_file_set_if_error(QEMUFile *f, int ret)
|
|
||||||
{
|
|
||||||
if (ret < 0 && !f->last_error) {
|
|
||||||
qemu_file_set_error(f, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Flushes QEMUFile buffer
|
/** Flushes QEMUFile buffer
|
||||||
*
|
*
|
||||||
* In case of error, last_error is set.
|
|
||||||
*/
|
*/
|
||||||
void qemu_fflush(QEMUFile *f)
|
static int qemu_fflush(QEMUFile *f)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!f->put_buffer)
|
if (!f->put_buffer)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if (f->is_write && f->buf_index > 0) {
|
if (f->is_write && f->buf_index > 0) {
|
||||||
int len;
|
ret = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
|
||||||
|
if (ret >= 0) {
|
||||||
len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
|
|
||||||
if (len > 0)
|
|
||||||
f->buf_offset += f->buf_index;
|
f->buf_offset += f->buf_index;
|
||||||
else
|
}
|
||||||
qemu_file_set_error(f, -EINVAL);
|
|
||||||
f->buf_index = 0;
|
f->buf_index = 0;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_fill_buffer(QEMUFile *f)
|
static void qemu_fill_buffer(QEMUFile *f)
|
||||||
|
@ -502,27 +489,11 @@ static void qemu_fill_buffer(QEMUFile *f)
|
||||||
f->buf_size += len;
|
f->buf_size += len;
|
||||||
f->buf_offset += len;
|
f->buf_offset += len;
|
||||||
} else if (len == 0) {
|
} else if (len == 0) {
|
||||||
f->last_error = -EIO;
|
qemu_file_set_error(f, -EIO);
|
||||||
} else if (len != -EAGAIN)
|
} else if (len != -EAGAIN)
|
||||||
qemu_file_set_error(f, len);
|
qemu_file_set_error(f, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calls close function and set last_error if needed
|
|
||||||
*
|
|
||||||
* Internal function. qemu_fflush() must be called before this.
|
|
||||||
*
|
|
||||||
* Returns f->close() return value, or 0 if close function is not set.
|
|
||||||
*/
|
|
||||||
static int qemu_fclose_internal(QEMUFile *f)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
if (f->close) {
|
|
||||||
ret = f->close(f->opaque);
|
|
||||||
qemu_file_set_if_error(f, ret);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Closes the file
|
/** Closes the file
|
||||||
*
|
*
|
||||||
* Returns negative error value if any error happened on previous operations or
|
* Returns negative error value if any error happened on previous operations or
|
||||||
|
@ -534,8 +505,14 @@ static int qemu_fclose_internal(QEMUFile *f)
|
||||||
int qemu_fclose(QEMUFile *f)
|
int qemu_fclose(QEMUFile *f)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
qemu_fflush(f);
|
ret = qemu_fflush(f);
|
||||||
ret = qemu_fclose_internal(f);
|
|
||||||
|
if (f->close) {
|
||||||
|
int ret2 = f->close(f->opaque);
|
||||||
|
if (ret >= 0) {
|
||||||
|
ret = ret2;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* If any error was spotted before closing, we should report it
|
/* If any error was spotted before closing, we should report it
|
||||||
* instead of the close() return value.
|
* instead of the close() return value.
|
||||||
*/
|
*/
|
||||||
|
@ -546,22 +523,26 @@ int qemu_fclose(QEMUFile *f)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_file_put_notify(QEMUFile *f)
|
int qemu_file_put_notify(QEMUFile *f)
|
||||||
{
|
{
|
||||||
f->put_buffer(f->opaque, NULL, 0, 0);
|
return f->put_buffer(f->opaque, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
|
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
|
||||||
{
|
{
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
|
if (f->last_error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->is_write == 0 && f->buf_index > 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Attempted to write to buffer while read buffer is not empty\n");
|
"Attempted to write to buffer while read buffer is not empty\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!f->last_error && size > 0) {
|
while (size > 0) {
|
||||||
l = IO_BUF_SIZE - f->buf_index;
|
l = IO_BUF_SIZE - f->buf_index;
|
||||||
if (l > size)
|
if (l > size)
|
||||||
l = size;
|
l = size;
|
||||||
|
@ -570,14 +551,23 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
|
||||||
f->buf_index += l;
|
f->buf_index += l;
|
||||||
buf += l;
|
buf += l;
|
||||||
size -= l;
|
size -= l;
|
||||||
if (f->buf_index >= IO_BUF_SIZE)
|
if (f->buf_index >= IO_BUF_SIZE) {
|
||||||
qemu_fflush(f);
|
int ret = qemu_fflush(f);
|
||||||
|
if (ret < 0) {
|
||||||
|
qemu_file_set_error(f, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_put_byte(QEMUFile *f, int v)
|
void qemu_put_byte(QEMUFile *f, int v)
|
||||||
{
|
{
|
||||||
if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
|
if (f->last_error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->is_write == 0 && f->buf_index > 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Attempted to write to buffer while read buffer is not empty\n");
|
"Attempted to write to buffer while read buffer is not empty\n");
|
||||||
abort();
|
abort();
|
||||||
|
@ -585,8 +575,12 @@ void qemu_put_byte(QEMUFile *f, int v)
|
||||||
|
|
||||||
f->buf[f->buf_index++] = v;
|
f->buf[f->buf_index++] = v;
|
||||||
f->is_write = 1;
|
f->is_write = 1;
|
||||||
if (f->buf_index >= IO_BUF_SIZE)
|
if (f->buf_index >= IO_BUF_SIZE) {
|
||||||
qemu_fflush(f);
|
int ret = qemu_fflush(f);
|
||||||
|
if (ret < 0) {
|
||||||
|
qemu_file_set_error(f, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_file_skip(QEMUFile *f, int size)
|
static void qemu_file_skip(QEMUFile *f, int size)
|
||||||
|
@ -671,32 +665,11 @@ int qemu_get_byte(QEMUFile *f)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t qemu_ftell(QEMUFile *f)
|
static int64_t qemu_ftell(QEMUFile *f)
|
||||||
{
|
{
|
||||||
return f->buf_offset - f->buf_size + f->buf_index;
|
return f->buf_offset - f->buf_size + f->buf_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
|
|
||||||
{
|
|
||||||
if (whence == SEEK_SET) {
|
|
||||||
/* nothing to do */
|
|
||||||
} else if (whence == SEEK_CUR) {
|
|
||||||
pos += qemu_ftell(f);
|
|
||||||
} else {
|
|
||||||
/* SEEK_END not supported */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (f->put_buffer) {
|
|
||||||
qemu_fflush(f);
|
|
||||||
f->buf_offset = pos;
|
|
||||||
} else {
|
|
||||||
f->buf_offset = pos;
|
|
||||||
f->buf_index = 0;
|
|
||||||
f->buf_size = 0;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qemu_file_rate_limit(QEMUFile *f)
|
int qemu_file_rate_limit(QEMUFile *f)
|
||||||
{
|
{
|
||||||
if (f->rate_limit)
|
if (f->rate_limit)
|
||||||
|
|
|
@ -921,6 +921,10 @@ ppm_save(const char *filename, void *display_surface) "%s surface=%p"
|
||||||
savevm_section_start(void) ""
|
savevm_section_start(void) ""
|
||||||
savevm_section_end(unsigned int section_id) "section_id %u"
|
savevm_section_end(unsigned int section_id) "section_id %u"
|
||||||
|
|
||||||
|
# arch_init.c
|
||||||
|
migration_bitmap_sync_start(void) ""
|
||||||
|
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64""
|
||||||
|
|
||||||
# hw/qxl.c
|
# hw/qxl.c
|
||||||
disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"
|
disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"
|
||||||
disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u"
|
disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u"
|
||||||
|
|
Loading…
Reference in New Issue