migration.next for 20140113
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJS1CSTAAoJEPSH7xhYctcjZGMP/2TM4JZla0HR8UPWaff5gbSO 8+qd5Sdy3kw5aK+5IoB0Dit/x09JmVHFJPFJugjOnT/0EWC/9My1wanQMxRDTAZK T5uksWYcfV5IcbTC5CyvSMKZZ0DEVcTvl46vFdrn9UySAzuL3UJYqnpFjiwOHV2R HG3K7eIgGRg9pcdfFqquhk4KOnnGwyNVQh/F8QOPzILgbD/JLLHuYWTn9dwFSezN 10EdEgA/+U+wXISrAANtjhojnE7SBuzTr/IjwAwijR4aUoTNqbmKmTZ8O4nxJkiv nXeDjXbzCsS1GblSZczNX1gfR/rLuYysMRPya2YV2rMXFNqpOzIQ63uzomsL1Jsm o9i9QBsvceb+qULTkC1I3n824cUrs8hxWttNqPZYFySOV/YROqsqMMwVv6h+UymU cHqkr8lmn7O4T9/yYUnjeJy65osSUgdtJw7OnL4PJJ2+cgkuF5heQBjqi3jlruFd YXv/fr8gYxaPvC99BfZ2ztonqT4mIa1zJoXSuxOeEU3/1BCa44SE0YKClojvRxDr WqSyQCyyCyadYpQrXuDp1B1FixyDsSOjIbZS16A5s3vbDHaJySdHx+FsBXNi3Gtb niWIY9mtlxYl7WOrBeD3GsrqVdPznfCNK+s7w8JFILAZflLeOzU/kqYXV4KJWdcK 5ay3YQ0MAxDpaRzjERxK =T0a8 -----END PGP SIGNATURE----- Merge remote-tracking branch 'quintela/tags/migration/20140113' into staging migration.next for 20140113 # gpg: Signature made Mon 13 Jan 2014 09:38:27 AM PST using RSA key ID 5872D723 # gpg: Can't check signature: public key not found * quintela/tags/migration/20140113: (49 commits) migration: synchronize memory bitmap 64bits at a time ram: split function that synchronizes a range memory: syncronize kvm bitmap using bitmaps operations memory: move bitmap synchronization to its own function kvm: refactor start address calculation kvm: use directly cpu_physical_memory_* api for tracking dirty pages memory: unfold memory_region_test_and_clear() memory: split cpu_physical_memory_* functions to its own include memory: cpu_physical_memory_set_dirty_tracking() should return void memory: make cpu_physical_memory_reset_dirty() take a length parameter memory: s/dirty/clean/ in cpu_physical_memory_is_dirty() memory: cpu_physical_memory_clear_dirty_range() now uses bitmap operations memory: cpu_physical_memory_set_dirty_range() now uses bitmap operations memory: use find_next_bit() to find dirty bits memory: s/mask/clear/ cpu_physical_memory_mask_dirty_range memory: cpu_physical_memory_get_dirty() is used as returning a bool memory: make cpu_physical_memory_get_dirty() the main function memory: unfold cpu_physical_memory_set_dirty_flag() memory: unfold cpu_physical_memory_set_dirty() in its only user memory: unfold cpu_physical_memory_clear_dirty_flag() in its only user ... Message-id: 1389634834-24181-1-git-send-email-quintela@redhat.com Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
commit
b54f18ba34
|
@ -51,6 +51,8 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
|
|||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration.o migration-tcp.o
|
||||
common-obj-y += vmstate.o
|
||||
common-obj-y += qemu-file.o
|
||||
common-obj-$(CONFIG_RDMA) += migration-rdma.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o
|
||||
|
|
52
arch_init.c
52
arch_init.c
|
@ -48,7 +48,9 @@
|
|||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "exec/cpu-all.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
#ifdef DEBUG_ARCH_INIT
|
||||
#define DPRINTF(fmt, ...) \
|
||||
|
@ -359,11 +361,10 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
|
|||
return (next - base) << TARGET_PAGE_BITS;
|
||||
}
|
||||
|
||||
static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
||||
ram_addr_t offset)
|
||||
static inline bool migration_bitmap_set_dirty(ram_addr_t addr)
|
||||
{
|
||||
bool ret;
|
||||
int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
|
||||
int nr = addr >> TARGET_PAGE_BITS;
|
||||
|
||||
ret = test_and_set_bit(nr, migration_bitmap);
|
||||
|
||||
|
@ -373,12 +374,47 @@ static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
|
||||
|
||||
/* start address is aligned at the start of a word? */
|
||||
if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) {
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS);
|
||||
unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION];
|
||||
|
||||
for (k = page; k < page + nr; k++) {
|
||||
if (src[k]) {
|
||||
unsigned long new_dirty;
|
||||
new_dirty = ~migration_bitmap[k];
|
||||
migration_bitmap[k] |= src[k];
|
||||
new_dirty &= src[k];
|
||||
migration_dirty_pages += ctpopl(new_dirty);
|
||||
src[k] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) {
|
||||
if (cpu_physical_memory_get_dirty(start + addr,
|
||||
TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
cpu_physical_memory_reset_dirty(start + addr,
|
||||
TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION);
|
||||
migration_bitmap_set_dirty(start + addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Needs iothread lock! */
|
||||
|
||||
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;
|
||||
|
@ -399,13 +435,7 @@ static void migration_bitmap_sync(void)
|
|||
address_space_sync_dirty_bitmap(&address_space_memory);
|
||||
|
||||
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
|
||||
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
|
||||
if (memory_region_test_and_clear_dirty(block->mr,
|
||||
addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_MIGRATION)) {
|
||||
migration_bitmap_set_dirty(block->mr, addr);
|
||||
}
|
||||
}
|
||||
migration_bitmap_sync_range(block->mr->ram_addr, block->length);
|
||||
}
|
||||
trace_migration_bitmap_sync_end(migration_dirty_pages
|
||||
- num_dirty_pages_init);
|
||||
|
|
11
cputlb.c
11
cputlb.c
|
@ -26,6 +26,7 @@
|
|||
#include "exec/cputlb.h"
|
||||
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
|
||||
//#define DEBUG_TLB
|
||||
//#define DEBUG_TLB_CHECK
|
||||
|
@ -112,9 +113,8 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr)
|
|||
can be detected */
|
||||
void tlb_protect_code(ram_addr_t ram_addr)
|
||||
{
|
||||
cpu_physical_memory_reset_dirty(ram_addr,
|
||||
ram_addr + TARGET_PAGE_SIZE,
|
||||
CODE_DIRTY_FLAG);
|
||||
cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE,
|
||||
DIRTY_MEMORY_CODE);
|
||||
}
|
||||
|
||||
/* update the TLB so that writes in physical page 'phys_addr' are no longer
|
||||
|
@ -122,7 +122,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
|
|||
void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr,
|
||||
target_ulong vaddr)
|
||||
{
|
||||
cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG);
|
||||
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
|
||||
}
|
||||
|
||||
static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe)
|
||||
|
@ -284,7 +284,8 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
|||
/* Write access calls the I/O callback. */
|
||||
te->addr_write = address | TLB_MMIO;
|
||||
} else if (memory_region_is_ram(section->mr)
|
||||
&& !cpu_physical_memory_is_dirty(section->mr->ram_addr + xlat)) {
|
||||
&& cpu_physical_memory_is_clean(section->mr->ram_addr
|
||||
+ xlat)) {
|
||||
te->addr_write = address | TLB_NOTDIRTY;
|
||||
} else {
|
||||
te->addr_write = address;
|
||||
|
|
76
exec.c
76
exec.c
|
@ -50,6 +50,7 @@
|
|||
#include "translate-all.h"
|
||||
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "qemu/cache-utils.h"
|
||||
|
||||
#include "qemu/range.h"
|
||||
|
@ -57,7 +58,7 @@
|
|||
//#define DEBUG_SUBPAGE
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static int in_migration;
|
||||
static bool in_migration;
|
||||
|
||||
RAMList ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) };
|
||||
|
||||
|
@ -724,11 +725,14 @@ found:
|
|||
return block;
|
||||
}
|
||||
|
||||
static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
|
||||
uintptr_t length)
|
||||
static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length)
|
||||
{
|
||||
RAMBlock *block;
|
||||
ram_addr_t start1;
|
||||
RAMBlock *block;
|
||||
ram_addr_t end;
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length);
|
||||
start &= TARGET_PAGE_MASK;
|
||||
|
||||
block = qemu_get_ram_block(start);
|
||||
assert(block == qemu_get_ram_block(end - 1));
|
||||
|
@ -737,29 +741,21 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
|
|||
}
|
||||
|
||||
/* Note: start and end must be within the same ram block. */
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags)
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
|
||||
unsigned client)
|
||||
{
|
||||
uintptr_t length;
|
||||
|
||||
start &= TARGET_PAGE_MASK;
|
||||
end = TARGET_PAGE_ALIGN(end);
|
||||
|
||||
length = end - start;
|
||||
if (length == 0)
|
||||
return;
|
||||
cpu_physical_memory_mask_dirty_range(start, length, dirty_flags);
|
||||
cpu_physical_memory_clear_dirty_range(start, length, client);
|
||||
|
||||
if (tcg_enabled()) {
|
||||
tlb_reset_dirty_range_all(start, end, length);
|
||||
tlb_reset_dirty_range_all(start, length);
|
||||
}
|
||||
}
|
||||
|
||||
static int cpu_physical_memory_set_dirty_tracking(int enable)
|
||||
static void cpu_physical_memory_set_dirty_tracking(bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
in_migration = enable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
hwaddr memory_region_section_get_iotlb(CPUArchState *env,
|
||||
|
@ -1211,6 +1207,9 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
|||
MemoryRegion *mr)
|
||||
{
|
||||
RAMBlock *block, *new_block;
|
||||
ram_addr_t old_ram_size, new_ram_size;
|
||||
|
||||
old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||
|
||||
size = TARGET_PAGE_ALIGN(size);
|
||||
new_block = g_malloc0(sizeof(*new_block));
|
||||
|
@ -1271,11 +1270,17 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
|||
ram_list.version++;
|
||||
qemu_mutex_unlock_ramlist();
|
||||
|
||||
ram_list.phys_dirty = g_realloc(ram_list.phys_dirty,
|
||||
last_ram_offset() >> TARGET_PAGE_BITS);
|
||||
memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
|
||||
0, size >> TARGET_PAGE_BITS);
|
||||
cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff);
|
||||
new_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||
|
||||
if (new_ram_size > old_ram_size) {
|
||||
int i;
|
||||
for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
|
||||
ram_list.dirty_memory[i] =
|
||||
bitmap_zero_extend(ram_list.dirty_memory[i],
|
||||
old_ram_size, new_ram_size);
|
||||
}
|
||||
}
|
||||
cpu_physical_memory_set_dirty_range(new_block->offset, size);
|
||||
|
||||
qemu_ram_setup_dump(new_block->host, size);
|
||||
qemu_madvise(new_block->host, size, QEMU_MADV_HUGEPAGE);
|
||||
|
@ -1485,11 +1490,8 @@ found:
|
|||
static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
int dirty_flags;
|
||||
dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
|
||||
if (!(dirty_flags & CODE_DIRTY_FLAG)) {
|
||||
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
||||
tb_invalidate_phys_page_fast(ram_addr, size);
|
||||
dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
|
||||
}
|
||||
switch (size) {
|
||||
case 1:
|
||||
|
@ -1504,11 +1506,11 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
|
|||
default:
|
||||
abort();
|
||||
}
|
||||
dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
|
||||
cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags);
|
||||
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_MIGRATION);
|
||||
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_VGA);
|
||||
/* we remove the notdirty callback only if the code has been
|
||||
flushed */
|
||||
if (dirty_flags == 0xff) {
|
||||
if (!cpu_physical_memory_is_clean(ram_addr)) {
|
||||
CPUArchState *env = current_cpu->env_ptr;
|
||||
tlb_set_dirty(env, env->mem_io_vaddr);
|
||||
}
|
||||
|
@ -1795,12 +1797,12 @@ static void tcg_commit(MemoryListener *listener)
|
|||
|
||||
static void core_log_global_start(MemoryListener *listener)
|
||||
{
|
||||
cpu_physical_memory_set_dirty_tracking(1);
|
||||
cpu_physical_memory_set_dirty_tracking(true);
|
||||
}
|
||||
|
||||
static void core_log_global_stop(MemoryListener *listener)
|
||||
{
|
||||
cpu_physical_memory_set_dirty_tracking(0);
|
||||
cpu_physical_memory_set_dirty_tracking(false);
|
||||
}
|
||||
|
||||
static MemoryListener core_memory_listener = {
|
||||
|
@ -1911,11 +1913,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|||
static void invalidate_and_set_dirty(hwaddr addr,
|
||||
hwaddr length)
|
||||
{
|
||||
if (!cpu_physical_memory_is_dirty(addr)) {
|
||||
if (cpu_physical_memory_is_clean(addr)) {
|
||||
/* invalidate code */
|
||||
tb_invalidate_phys_page_range(addr, addr + length, 0);
|
||||
/* set dirty bit */
|
||||
cpu_physical_memory_set_dirty_flags(addr, (0xff & ~CODE_DIRTY_FLAG));
|
||||
cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA);
|
||||
cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION);
|
||||
}
|
||||
xen_modified_memory(addr, length);
|
||||
}
|
||||
|
@ -2526,12 +2529,13 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val)
|
|||
stl_p(ptr, val);
|
||||
|
||||
if (unlikely(in_migration)) {
|
||||
if (!cpu_physical_memory_is_dirty(addr1)) {
|
||||
if (cpu_physical_memory_is_clean(addr1)) {
|
||||
/* invalidate code */
|
||||
tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
|
||||
/* set dirty bit */
|
||||
cpu_physical_memory_set_dirty_flags(
|
||||
addr1, (0xff & ~CODE_DIRTY_FLAG));
|
||||
cpu_physical_memory_set_dirty_flag(addr1,
|
||||
DIRTY_MEMORY_MIGRATION);
|
||||
cpu_physical_memory_set_dirty_flag(addr1, DIRTY_MEMORY_VGA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "qemu-common.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "exec/memory.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
|
@ -459,7 +460,7 @@ typedef struct RAMBlock {
|
|||
typedef struct RAMList {
|
||||
QemuMutex mutex;
|
||||
/* Protected by the iothread lock. */
|
||||
uint8_t *phys_dirty;
|
||||
unsigned long *dirty_memory[DIRTY_MEMORY_NUM];
|
||||
RAMBlock *mru_block;
|
||||
/* Protected by the ramlist lock. */
|
||||
QTAILQ_HEAD(, RAMBlock) blocks;
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#define MEMORY_INTERNAL_H
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "hw/xen/xen.h"
|
||||
|
||||
|
||||
typedef struct AddressSpaceDispatch AddressSpaceDispatch;
|
||||
|
||||
void address_space_init_dispatch(AddressSpace *as);
|
||||
|
@ -33,92 +30,5 @@ extern const MemoryRegionOps unassigned_mem_ops;
|
|||
bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr,
|
||||
unsigned size, bool is_write);
|
||||
|
||||
ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||
MemoryRegion *mr);
|
||||
ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
|
||||
void *qemu_get_ram_ptr(ram_addr_t addr);
|
||||
void qemu_ram_free(ram_addr_t addr);
|
||||
void qemu_ram_free_from_ptr(ram_addr_t addr);
|
||||
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
#define CODE_DIRTY_FLAG 0x02
|
||||
#define MIGRATION_DIRTY_FLAG 0x08
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
|
||||
}
|
||||
|
||||
/* read dirty bit (return 0 or 1) */
|
||||
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
|
||||
{
|
||||
return cpu_physical_memory_get_dirty_flags(addr) == 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty(ram_addr_t start,
|
||||
ram_addr_t length,
|
||||
int dirty_flags)
|
||||
{
|
||||
int ret = 0;
|
||||
ram_addr_t addr, end;
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length);
|
||||
start &= TARGET_PAGE_MASK;
|
||||
for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
|
||||
ret |= cpu_physical_memory_get_dirty_flags(addr) & dirty_flags;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
|
||||
{
|
||||
cpu_physical_memory_set_dirty_flags(addr, 0xff);
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
int mask = ~dirty_flags;
|
||||
|
||||
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
|
||||
ram_addr_t length,
|
||||
int dirty_flags)
|
||||
{
|
||||
ram_addr_t addr, end;
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length);
|
||||
start &= TARGET_PAGE_MASK;
|
||||
for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
|
||||
cpu_physical_memory_set_dirty_flags(addr, dirty_flags);
|
||||
}
|
||||
xen_modified_memory(addr, length);
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
|
||||
ram_addr_t length,
|
||||
int dirty_flags)
|
||||
{
|
||||
ram_addr_t addr, end;
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length);
|
||||
start &= TARGET_PAGE_MASK;
|
||||
for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
|
||||
cpu_physical_memory_clear_dirty_flags(addr, dirty_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
#define DIRTY_MEMORY_VGA 0
|
||||
#define DIRTY_MEMORY_CODE 1
|
||||
#define DIRTY_MEMORY_MIGRATION 2
|
||||
#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "qemu-common.h"
|
||||
|
@ -33,13 +38,6 @@
|
|||
typedef struct MemoryRegionOps MemoryRegionOps;
|
||||
typedef struct MemoryRegionMmio MemoryRegionMmio;
|
||||
|
||||
/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic
|
||||
* registration.
|
||||
*/
|
||||
#define DIRTY_MEMORY_VGA 0
|
||||
#define DIRTY_MEMORY_CODE 1
|
||||
#define DIRTY_MEMORY_MIGRATION 3
|
||||
|
||||
struct MemoryRegionMmio {
|
||||
CPUReadMemoryFunc *read[3];
|
||||
CPUWriteMemoryFunc *write[3];
|
||||
|
|
147
include/exec/ram_addr.h
Normal file
147
include/exec/ram_addr.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Declarations for cpu physical memory functions
|
||||
*
|
||||
* Copyright 2011 Red Hat, Inc. and/or its affiliates
|
||||
*
|
||||
* Authors:
|
||||
* Avi Kivity <avi@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header is for use by exec.c and memory.c ONLY. Do not include it.
|
||||
* The functions declared here will be removed soon.
|
||||
*/
|
||||
|
||||
#ifndef RAM_ADDR_H
|
||||
#define RAM_ADDR_H
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "hw/xen/xen.h"
|
||||
|
||||
ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||
MemoryRegion *mr);
|
||||
ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
|
||||
void *qemu_get_ram_ptr(ram_addr_t addr);
|
||||
void qemu_ram_free(ram_addr_t addr);
|
||||
void qemu_ram_free_from_ptr(ram_addr_t addr);
|
||||
|
||||
static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
|
||||
ram_addr_t length,
|
||||
unsigned client)
|
||||
{
|
||||
unsigned long end, page, next;
|
||||
|
||||
assert(client < DIRTY_MEMORY_NUM);
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
|
||||
page = start >> TARGET_PAGE_BITS;
|
||||
next = find_next_bit(ram_list.dirty_memory[client], end, page);
|
||||
|
||||
return next < end;
|
||||
}
|
||||
|
||||
static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr,
|
||||
unsigned client)
|
||||
{
|
||||
return cpu_physical_memory_get_dirty(addr, 1, client);
|
||||
}
|
||||
|
||||
static inline bool cpu_physical_memory_is_clean(ram_addr_t addr)
|
||||
{
|
||||
bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA);
|
||||
bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE);
|
||||
bool migration =
|
||||
cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION);
|
||||
return !(vga && code && migration);
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr,
|
||||
unsigned client)
|
||||
{
|
||||
assert(client < DIRTY_MEMORY_NUM);
|
||||
set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]);
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
|
||||
ram_addr_t length)
|
||||
{
|
||||
unsigned long end, page;
|
||||
|
||||
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
|
||||
page = start >> TARGET_PAGE_BITS;
|
||||
bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page);
|
||||
bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page);
|
||||
bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page);
|
||||
xen_modified_memory(start, length);
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
|
||||
ram_addr_t start,
|
||||
ram_addr_t pages)
|
||||
{
|
||||
unsigned long i, j;
|
||||
unsigned long page_number, c;
|
||||
hwaddr addr;
|
||||
ram_addr_t ram_addr;
|
||||
unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS;
|
||||
unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE;
|
||||
unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
|
||||
|
||||
/* start address is aligned at the start of a word? */
|
||||
if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) {
|
||||
long k;
|
||||
long nr = BITS_TO_LONGS(pages);
|
||||
|
||||
for (k = 0; k < nr; k++) {
|
||||
if (bitmap[k]) {
|
||||
unsigned long temp = leul_to_cpu(bitmap[k]);
|
||||
|
||||
ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp;
|
||||
ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp;
|
||||
ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp;
|
||||
}
|
||||
}
|
||||
xen_modified_memory(start, pages);
|
||||
} else {
|
||||
/*
|
||||
* bitmap-traveling is faster than memory-traveling (for addr...)
|
||||
* especially when most of the memory is not dirty.
|
||||
*/
|
||||
for (i = 0; i < len; i++) {
|
||||
if (bitmap[i] != 0) {
|
||||
c = leul_to_cpu(bitmap[i]);
|
||||
do {
|
||||
j = ffsl(c) - 1;
|
||||
c &= ~(1ul << j);
|
||||
page_number = (i * HOST_LONG_BITS + j) * hpratio;
|
||||
addr = page_number * TARGET_PAGE_SIZE;
|
||||
ram_addr = start + addr;
|
||||
cpu_physical_memory_set_dirty_range(ram_addr,
|
||||
TARGET_PAGE_SIZE * hpratio);
|
||||
} while (c != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start,
|
||||
ram_addr_t length,
|
||||
unsigned client)
|
||||
{
|
||||
unsigned long end, page;
|
||||
|
||||
assert(client < DIRTY_MEMORY_NUM);
|
||||
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
|
||||
page = start >> TARGET_PAGE_BITS;
|
||||
bitmap_clear(ram_list.dirty_memory[client], page, end - page);
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
|
||||
unsigned client);
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -23,6 +23,17 @@
|
|||
#include "qapi-types.h"
|
||||
#include "exec/cpu-common.h"
|
||||
|
||||
#define QEMU_VM_FILE_MAGIC 0x5145564d
|
||||
#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
|
||||
#define QEMU_VM_FILE_VERSION 0x00000003
|
||||
|
||||
#define QEMU_VM_EOF 0x00
|
||||
#define QEMU_VM_SECTION_START 0x01
|
||||
#define QEMU_VM_SECTION_PART 0x02
|
||||
#define QEMU_VM_SECTION_END 0x03
|
||||
#define QEMU_VM_SECTION_FULL 0x04
|
||||
#define QEMU_VM_SUBSECTION 0x05
|
||||
|
||||
struct MigrationParams {
|
||||
bool blk;
|
||||
bool shared;
|
||||
|
|
|
@ -121,8 +121,11 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
|
|||
void qemu_put_be16(QEMUFile *f, unsigned int v);
|
||||
void qemu_put_be32(QEMUFile *f, unsigned int v);
|
||||
void qemu_put_be64(QEMUFile *f, uint64_t v);
|
||||
int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset);
|
||||
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
|
||||
int qemu_peek_byte(QEMUFile *f, int offset);
|
||||
int qemu_get_byte(QEMUFile *f);
|
||||
void qemu_file_skip(QEMUFile *f, int size);
|
||||
void qemu_update_position(QEMUFile *f, size_t size);
|
||||
|
||||
static inline unsigned int qemu_get_ubyte(QEMUFile *f)
|
||||
|
@ -141,6 +144,7 @@ void qemu_file_reset_rate_limit(QEMUFile *f);
|
|||
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
|
||||
int64_t qemu_file_get_rate_limit(QEMUFile *f);
|
||||
int qemu_file_get_error(QEMUFile *f);
|
||||
void qemu_file_set_error(QEMUFile *f, int ret);
|
||||
void qemu_fflush(QEMUFile *f);
|
||||
|
||||
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
|
||||
|
|
|
@ -67,66 +67,66 @@
|
|||
#define small_nbits(nbits) \
|
||||
((nbits) <= BITS_PER_LONG)
|
||||
|
||||
int slow_bitmap_empty(const unsigned long *bitmap, int bits);
|
||||
int slow_bitmap_full(const unsigned long *bitmap, int bits);
|
||||
int slow_bitmap_empty(const unsigned long *bitmap, long bits);
|
||||
int slow_bitmap_full(const unsigned long *bitmap, long bits);
|
||||
int slow_bitmap_equal(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
|
||||
int bits);
|
||||
long bits);
|
||||
void slow_bitmap_shift_right(unsigned long *dst,
|
||||
const unsigned long *src, int shift, int bits);
|
||||
const unsigned long *src, int shift, long bits);
|
||||
void slow_bitmap_shift_left(unsigned long *dst,
|
||||
const unsigned long *src, int shift, int bits);
|
||||
const unsigned long *src, int shift, long bits);
|
||||
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
int slow_bitmap_intersects(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits);
|
||||
const unsigned long *bitmap2, long bits);
|
||||
|
||||
static inline unsigned long *bitmap_new(int nbits)
|
||||
static inline unsigned long *bitmap_new(long nbits)
|
||||
{
|
||||
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
return g_malloc0(len);
|
||||
}
|
||||
|
||||
static inline void bitmap_zero(unsigned long *dst, int nbits)
|
||||
static inline void bitmap_zero(unsigned long *dst, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
*dst = 0UL;
|
||||
} else {
|
||||
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
memset(dst, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bitmap_fill(unsigned long *dst, int nbits)
|
||||
static inline void bitmap_fill(unsigned long *dst, long nbits)
|
||||
{
|
||||
size_t nlongs = BITS_TO_LONGS(nbits);
|
||||
if (!small_nbits(nbits)) {
|
||||
int len = (nlongs - 1) * sizeof(unsigned long);
|
||||
long len = (nlongs - 1) * sizeof(unsigned long);
|
||||
memset(dst, 0xff, len);
|
||||
}
|
||||
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
|
||||
}
|
||||
|
||||
static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
|
||||
int nbits)
|
||||
long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
*dst = *src;
|
||||
} else {
|
||||
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
memcpy(dst, src, len);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return (*dst = *src1 & *src2) != 0;
|
||||
|
@ -135,7 +135,7 @@ static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
|
|||
}
|
||||
|
||||
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
*dst = *src1 | *src2;
|
||||
|
@ -145,7 +145,7 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
|
|||
}
|
||||
|
||||
static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
*dst = *src1 ^ *src2;
|
||||
|
@ -155,7 +155,7 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
|
|||
}
|
||||
|
||||
static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return (*dst = *src1 & ~(*src2)) != 0;
|
||||
|
@ -163,8 +163,9 @@ static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
|
|||
return slow_bitmap_andnot(dst, src1, src2, nbits);
|
||||
}
|
||||
|
||||
static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
|
||||
int nbits)
|
||||
static inline void bitmap_complement(unsigned long *dst,
|
||||
const unsigned long *src,
|
||||
long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
*dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits);
|
||||
|
@ -174,7 +175,7 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr
|
|||
}
|
||||
|
||||
static inline int bitmap_equal(const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
|
||||
|
@ -183,7 +184,7 @@ static inline int bitmap_equal(const unsigned long *src1,
|
|||
}
|
||||
}
|
||||
|
||||
static inline int bitmap_empty(const unsigned long *src, int nbits)
|
||||
static inline int bitmap_empty(const unsigned long *src, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
|
||||
|
@ -192,7 +193,7 @@ static inline int bitmap_empty(const unsigned long *src, int nbits)
|
|||
}
|
||||
}
|
||||
|
||||
static inline int bitmap_full(const unsigned long *src, int nbits)
|
||||
static inline int bitmap_full(const unsigned long *src, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
|
||||
|
@ -202,7 +203,7 @@ static inline int bitmap_full(const unsigned long *src, int nbits)
|
|||
}
|
||||
|
||||
static inline int bitmap_intersects(const unsigned long *src1,
|
||||
const unsigned long *src2, int nbits)
|
||||
const unsigned long *src2, long nbits)
|
||||
{
|
||||
if (small_nbits(nbits)) {
|
||||
return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
|
||||
|
@ -211,12 +212,21 @@ static inline int bitmap_intersects(const unsigned long *src1,
|
|||
}
|
||||
}
|
||||
|
||||
void bitmap_set(unsigned long *map, int i, int len);
|
||||
void bitmap_clear(unsigned long *map, int start, int nr);
|
||||
void bitmap_set(unsigned long *map, long i, long len);
|
||||
void bitmap_clear(unsigned long *map, long start, long nr);
|
||||
unsigned long bitmap_find_next_zero_area(unsigned long *map,
|
||||
unsigned long size,
|
||||
unsigned long start,
|
||||
unsigned int nr,
|
||||
unsigned long nr,
|
||||
unsigned long align_mask);
|
||||
|
||||
static inline unsigned long *bitmap_zero_extend(unsigned long *old,
|
||||
long old_nbits, long new_nbits)
|
||||
{
|
||||
long new_len = BITS_TO_LONGS(new_nbits) * sizeof(unsigned long);
|
||||
unsigned long *new = g_realloc(old, new_len);
|
||||
bitmap_clear(new, old_nbits, new_nbits - old_nbits);
|
||||
return new;
|
||||
}
|
||||
|
||||
#endif /* BITMAP_H */
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* @nr: the bit to set
|
||||
* @addr: the address to start counting from
|
||||
*/
|
||||
static inline void set_bit(int nr, unsigned long *addr)
|
||||
static inline void set_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -41,7 +41,7 @@ static inline void set_bit(int nr, unsigned long *addr)
|
|||
* @nr: Bit to clear
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline void clear_bit(int nr, unsigned long *addr)
|
||||
static inline void clear_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -54,7 +54,7 @@ static inline void clear_bit(int nr, unsigned long *addr)
|
|||
* @nr: Bit to change
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline void change_bit(int nr, unsigned long *addr)
|
||||
static inline void change_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -67,7 +67,7 @@ static inline void change_bit(int nr, unsigned long *addr)
|
|||
* @nr: Bit to set
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_set_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_set_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -82,7 +82,7 @@ static inline int test_and_set_bit(int nr, unsigned long *addr)
|
|||
* @nr: Bit to clear
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_clear_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_clear_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -97,7 +97,7 @@ static inline int test_and_clear_bit(int nr, unsigned long *addr)
|
|||
* @nr: Bit to change
|
||||
* @addr: Address to count from
|
||||
*/
|
||||
static inline int test_and_change_bit(int nr, unsigned long *addr)
|
||||
static inline int test_and_change_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
@ -112,7 +112,7 @@ static inline int test_and_change_bit(int nr, unsigned long *addr)
|
|||
* @nr: bit number to test
|
||||
* @addr: Address to start counting from
|
||||
*/
|
||||
static inline int test_bit(int nr, const unsigned long *addr)
|
||||
static inline int test_bit(long nr, const unsigned long *addr)
|
||||
{
|
||||
return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
|
||||
}
|
||||
|
|
28
kvm-all.c
28
kvm-all.c
|
@ -31,6 +31,7 @@
|
|||
#include "sysemu/kvm.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace.h"
|
||||
|
@ -379,31 +380,10 @@ static int kvm_set_migration_log(int enable)
|
|||
static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
unsigned long *bitmap)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned long page_number, c;
|
||||
hwaddr addr, addr1;
|
||||
unsigned int pages = int128_get64(section->size) / getpagesize();
|
||||
unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS;
|
||||
unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE;
|
||||
ram_addr_t start = section->offset_within_region + section->mr->ram_addr;
|
||||
ram_addr_t pages = int128_get64(section->size) / getpagesize();
|
||||
|
||||
/*
|
||||
* bitmap-traveling is faster than memory-traveling (for addr...)
|
||||
* especially when most of the memory is not dirty.
|
||||
*/
|
||||
for (i = 0; i < len; i++) {
|
||||
if (bitmap[i] != 0) {
|
||||
c = leul_to_cpu(bitmap[i]);
|
||||
do {
|
||||
j = ffsl(c) - 1;
|
||||
c &= ~(1ul << j);
|
||||
page_number = (i * HOST_LONG_BITS + j) * hpratio;
|
||||
addr1 = page_number * TARGET_PAGE_SIZE;
|
||||
addr = section->offset_within_region + addr1;
|
||||
memory_region_set_dirty(section->mr, addr,
|
||||
TARGET_PAGE_SIZE * hpratio);
|
||||
} while (c != 0);
|
||||
}
|
||||
}
|
||||
cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
17
memory.c
17
memory.c
|
@ -22,6 +22,7 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
|
||||
//#define DEBUG_UNASSIGNED
|
||||
|
||||
|
@ -1174,15 +1175,14 @@ bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
|
|||
hwaddr size, unsigned client)
|
||||
{
|
||||
assert(mr->terminates);
|
||||
return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
|
||||
1 << client);
|
||||
return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client);
|
||||
}
|
||||
|
||||
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
hwaddr size)
|
||||
{
|
||||
assert(mr->terminates);
|
||||
return cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1);
|
||||
cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size);
|
||||
}
|
||||
|
||||
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
|
@ -1190,12 +1190,9 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
|
|||
{
|
||||
bool ret;
|
||||
assert(mr->terminates);
|
||||
ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
|
||||
1 << client);
|
||||
ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client);
|
||||
if (ret) {
|
||||
cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
|
||||
mr->ram_addr + addr + size,
|
||||
1 << client);
|
||||
cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1241,9 +1238,7 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
|
|||
hwaddr size, unsigned client)
|
||||
{
|
||||
assert(mr->terminates);
|
||||
cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
|
||||
mr->ram_addr + addr + size,
|
||||
1 << client);
|
||||
cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
|
||||
}
|
||||
|
||||
void *memory_region_get_ram_ptr(MemoryRegion *mr)
|
||||
|
|
33
migration.c
33
migration.c
|
@ -40,6 +40,7 @@ enum {
|
|||
MIG_STATE_ERROR = -1,
|
||||
MIG_STATE_NONE,
|
||||
MIG_STATE_SETUP,
|
||||
MIG_STATE_CANCELLING,
|
||||
MIG_STATE_CANCELLED,
|
||||
MIG_STATE_ACTIVE,
|
||||
MIG_STATE_COMPLETED,
|
||||
|
@ -196,6 +197,7 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
|||
info->has_total_time = false;
|
||||
break;
|
||||
case MIG_STATE_ACTIVE:
|
||||
case MIG_STATE_CANCELLING:
|
||||
info->has_status = true;
|
||||
info->status = g_strdup("active");
|
||||
info->has_total_time = true;
|
||||
|
@ -282,6 +284,13 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
|
|||
|
||||
/* shared migration helpers */
|
||||
|
||||
static void migrate_set_state(MigrationState *s, int old_state, int new_state)
|
||||
{
|
||||
if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) {
|
||||
trace_migrate_set_state(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void migrate_fd_cleanup(void *opaque)
|
||||
{
|
||||
MigrationState *s = opaque;
|
||||
|
@ -303,18 +312,14 @@ static void migrate_fd_cleanup(void *opaque)
|
|||
|
||||
if (s->state != MIG_STATE_COMPLETED) {
|
||||
qemu_savevm_state_cancel();
|
||||
if (s->state == MIG_STATE_CANCELLING) {
|
||||
migrate_set_state(s, MIG_STATE_CANCELLING, MIG_STATE_CANCELLED);
|
||||
}
|
||||
}
|
||||
|
||||
notifier_list_notify(&migration_state_notifiers, s);
|
||||
}
|
||||
|
||||
static void migrate_set_state(MigrationState *s, int old_state, int new_state)
|
||||
{
|
||||
if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) {
|
||||
trace_migrate_set_state(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
void migrate_fd_error(MigrationState *s)
|
||||
{
|
||||
DPRINTF("setting error state\n");
|
||||
|
@ -326,9 +331,16 @@ void migrate_fd_error(MigrationState *s)
|
|||
|
||||
static void migrate_fd_cancel(MigrationState *s)
|
||||
{
|
||||
int old_state ;
|
||||
DPRINTF("cancelling migration\n");
|
||||
|
||||
migrate_set_state(s, s->state, MIG_STATE_CANCELLED);
|
||||
do {
|
||||
old_state = s->state;
|
||||
if (old_state != MIG_STATE_SETUP && old_state != MIG_STATE_ACTIVE) {
|
||||
break;
|
||||
}
|
||||
migrate_set_state(s, old_state, MIG_STATE_CANCELLING);
|
||||
} while (s->state != MIG_STATE_CANCELLING);
|
||||
}
|
||||
|
||||
void add_migration_state_change_notifier(Notifier *notify)
|
||||
|
@ -405,7 +417,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
|
|||
params.blk = has_blk && blk;
|
||||
params.shared = has_inc && inc;
|
||||
|
||||
if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP) {
|
||||
if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP ||
|
||||
s->state == MIG_STATE_CANCELLING) {
|
||||
error_set(errp, QERR_MIGRATION_ACTIVE);
|
||||
return;
|
||||
}
|
||||
|
@ -583,7 +596,7 @@ static void *migration_thread(void *opaque)
|
|||
|
||||
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||
if (ret >= 0) {
|
||||
qemu_file_set_rate_limit(s->file, INT_MAX);
|
||||
qemu_file_set_rate_limit(s->file, INT64_MAX);
|
||||
qemu_savevm_state_complete(s->file);
|
||||
}
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
|
826
qemu-file.c
Normal file
826
qemu-file.c
Normal file
|
@ -0,0 +1,826 @@
|
|||
#include "qemu-common.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/qemu-file.h"
|
||||
|
||||
#define IO_BUF_SIZE 32768
|
||||
#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
|
||||
|
||||
struct QEMUFile {
|
||||
const QEMUFileOps *ops;
|
||||
void *opaque;
|
||||
|
||||
int64_t bytes_xfer;
|
||||
int64_t xfer_limit;
|
||||
|
||||
int64_t pos; /* start of buffer when writing, end of buffer
|
||||
when reading */
|
||||
int buf_index;
|
||||
int buf_size; /* 0 when writing */
|
||||
uint8_t buf[IO_BUF_SIZE];
|
||||
|
||||
struct iovec iov[MAX_IOV_SIZE];
|
||||
unsigned int iovcnt;
|
||||
|
||||
int last_error;
|
||||
};
|
||||
|
||||
typedef struct QEMUFileStdio {
|
||||
FILE *stdio_file;
|
||||
QEMUFile *file;
|
||||
} QEMUFileStdio;
|
||||
|
||||
typedef struct QEMUFileSocket {
|
||||
int fd;
|
||||
QEMUFile *file;
|
||||
} QEMUFileSocket;
|
||||
|
||||
static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
|
||||
int64_t pos)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
ssize_t len;
|
||||
ssize_t size = iov_size(iov, iovcnt);
|
||||
|
||||
len = iov_send(s->fd, iov, iovcnt, 0, size);
|
||||
if (len < size) {
|
||||
len = -socket_error();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int socket_get_fd(void *opaque)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
|
||||
return s->fd;
|
||||
}
|
||||
|
||||
static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
ssize_t len;
|
||||
|
||||
for (;;) {
|
||||
len = qemu_recv(s->fd, buf, size, 0);
|
||||
if (len != -1) {
|
||||
break;
|
||||
}
|
||||
if (socket_error() == EAGAIN) {
|
||||
yield_until_fd_readable(s->fd);
|
||||
} else if (socket_error() != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
len = -socket_error();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int socket_close(void *opaque)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
closesocket(s->fd);
|
||||
g_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stdio_get_fd(void *opaque)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
|
||||
return fileno(s->stdio_file);
|
||||
}
|
||||
|
||||
static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos,
|
||||
int size)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
return fwrite(buf, 1, size, s->stdio_file);
|
||||
}
|
||||
|
||||
static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
FILE *fp = s->stdio_file;
|
||||
int bytes;
|
||||
|
||||
for (;;) {
|
||||
clearerr(fp);
|
||||
bytes = fread(buf, 1, size, fp);
|
||||
if (bytes != 0 || !ferror(fp)) {
|
||||
break;
|
||||
}
|
||||
if (errno == EAGAIN) {
|
||||
yield_until_fd_readable(fileno(fp));
|
||||
} else if (errno != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int stdio_pclose(void *opaque)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
int ret;
|
||||
ret = pclose(s->stdio_file);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
} else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
|
||||
/* close succeeded, but non-zero exit code: */
|
||||
ret = -EIO; /* fake errno value */
|
||||
}
|
||||
g_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stdio_fclose(void *opaque)
|
||||
{
|
||||
QEMUFileStdio *s = opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (s->file->ops->put_buffer || s->file->ops->writev_buffer) {
|
||||
int fd = fileno(s->stdio_file);
|
||||
struct stat st;
|
||||
|
||||
ret = fstat(fd, &st);
|
||||
if (ret == 0 && S_ISREG(st.st_mode)) {
|
||||
/*
|
||||
* If the file handle is a regular file make sure the
|
||||
* data is flushed to disk before signaling success.
|
||||
*/
|
||||
ret = fsync(fd);
|
||||
if (ret != 0) {
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fclose(s->stdio_file) == EOF) {
|
||||
ret = -errno;
|
||||
}
|
||||
g_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const QEMUFileOps stdio_pipe_read_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.get_buffer = stdio_get_buffer,
|
||||
.close = stdio_pclose
|
||||
};
|
||||
|
||||
static const QEMUFileOps stdio_pipe_write_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.put_buffer = stdio_put_buffer,
|
||||
.close = stdio_pclose
|
||||
};
|
||||
|
||||
QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
|
||||
{
|
||||
FILE *stdio_file;
|
||||
QEMUFileStdio *s;
|
||||
|
||||
if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
|
||||
fprintf(stderr, "qemu_popen: Argument validity check failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stdio_file = popen(command, mode);
|
||||
if (stdio_file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUFileStdio));
|
||||
|
||||
s->stdio_file = stdio_file;
|
||||
|
||||
if (mode[0] == 'r') {
|
||||
s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
|
||||
}
|
||||
return s->file;
|
||||
}
|
||||
|
||||
static const QEMUFileOps stdio_file_read_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.get_buffer = stdio_get_buffer,
|
||||
.close = stdio_fclose
|
||||
};
|
||||
|
||||
static const QEMUFileOps stdio_file_write_ops = {
|
||||
.get_fd = stdio_get_fd,
|
||||
.put_buffer = stdio_put_buffer,
|
||||
.close = stdio_fclose
|
||||
};
|
||||
|
||||
static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
|
||||
int64_t pos)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
ssize_t len, offset;
|
||||
ssize_t size = iov_size(iov, iovcnt);
|
||||
ssize_t total = 0;
|
||||
|
||||
assert(iovcnt > 0);
|
||||
offset = 0;
|
||||
while (size > 0) {
|
||||
/* Find the next start position; skip all full-sized vector elements */
|
||||
while (offset >= iov[0].iov_len) {
|
||||
offset -= iov[0].iov_len;
|
||||
iov++, iovcnt--;
|
||||
}
|
||||
|
||||
/* skip `offset' bytes from the (now) first element, undo it on exit */
|
||||
assert(iovcnt > 0);
|
||||
iov[0].iov_base += offset;
|
||||
iov[0].iov_len -= offset;
|
||||
|
||||
do {
|
||||
len = writev(s->fd, iov, iovcnt);
|
||||
} while (len == -1 && errno == EINTR);
|
||||
if (len == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Undo the changes above */
|
||||
iov[0].iov_base -= offset;
|
||||
iov[0].iov_len += offset;
|
||||
|
||||
/* Prepare for the next iteration */
|
||||
offset += len;
|
||||
total += len;
|
||||
size -= len;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
ssize_t len;
|
||||
|
||||
for (;;) {
|
||||
len = read(s->fd, buf, size);
|
||||
if (len != -1) {
|
||||
break;
|
||||
}
|
||||
if (errno == EAGAIN) {
|
||||
yield_until_fd_readable(s->fd);
|
||||
} else if (errno != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
len = -errno;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int unix_close(void *opaque)
|
||||
{
|
||||
QEMUFileSocket *s = opaque;
|
||||
close(s->fd);
|
||||
g_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const QEMUFileOps unix_read_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.get_buffer = unix_get_buffer,
|
||||
.close = unix_close
|
||||
};
|
||||
|
||||
static const QEMUFileOps unix_write_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.writev_buffer = unix_writev_buffer,
|
||||
.close = unix_close
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fdopen(int fd, const char *mode)
|
||||
{
|
||||
QEMUFileSocket *s;
|
||||
|
||||
if (mode == NULL ||
|
||||
(mode[0] != 'r' && mode[0] != 'w') ||
|
||||
mode[1] != 'b' || mode[2] != 0) {
|
||||
fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUFileSocket));
|
||||
s->fd = fd;
|
||||
|
||||
if (mode[0] == 'r') {
|
||||
s->file = qemu_fopen_ops(s, &unix_read_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &unix_write_ops);
|
||||
}
|
||||
return s->file;
|
||||
}
|
||||
|
||||
static const QEMUFileOps socket_read_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.get_buffer = socket_get_buffer,
|
||||
.close = socket_close
|
||||
};
|
||||
|
||||
static const QEMUFileOps socket_write_ops = {
|
||||
.get_fd = socket_get_fd,
|
||||
.writev_buffer = socket_writev_buffer,
|
||||
.close = socket_close
|
||||
};
|
||||
|
||||
bool qemu_file_mode_is_not_valid(const char *mode)
|
||||
{
|
||||
if (mode == NULL ||
|
||||
(mode[0] != 'r' && mode[0] != 'w') ||
|
||||
mode[1] != 'b' || mode[2] != 0) {
|
||||
fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QEMUFile *qemu_fopen_socket(int fd, const char *mode)
|
||||
{
|
||||
QEMUFileSocket *s;
|
||||
|
||||
if (qemu_file_mode_is_not_valid(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUFileSocket));
|
||||
s->fd = fd;
|
||||
if (mode[0] == 'w') {
|
||||
qemu_set_block(s->fd);
|
||||
s->file = qemu_fopen_ops(s, &socket_write_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &socket_read_ops);
|
||||
}
|
||||
return s->file;
|
||||
}
|
||||
|
||||
QEMUFile *qemu_fopen(const char *filename, const char *mode)
|
||||
{
|
||||
QEMUFileStdio *s;
|
||||
|
||||
if (qemu_file_mode_is_not_valid(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(QEMUFileStdio));
|
||||
|
||||
s->stdio_file = fopen(filename, mode);
|
||||
if (!s->stdio_file) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (mode[0] == 'w') {
|
||||
s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
|
||||
} else {
|
||||
s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
|
||||
}
|
||||
return s->file;
|
||||
fail:
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
|
||||
{
|
||||
QEMUFile *f;
|
||||
|
||||
f = g_malloc0(sizeof(QEMUFile));
|
||||
|
||||
f->opaque = opaque;
|
||||
f->ops = ops;
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get last error for stream f
|
||||
*
|
||||
* Return negative error value if there has been an error on previous
|
||||
* operations, return 0 if no error happened.
|
||||
*
|
||||
*/
|
||||
int qemu_file_get_error(QEMUFile *f)
|
||||
{
|
||||
return f->last_error;
|
||||
}
|
||||
|
||||
void qemu_file_set_error(QEMUFile *f, int ret)
|
||||
{
|
||||
if (f->last_error == 0) {
|
||||
f->last_error = ret;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool qemu_file_is_writable(QEMUFile *f)
|
||||
{
|
||||
return f->ops->writev_buffer || f->ops->put_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes QEMUFile buffer
|
||||
*
|
||||
* If there is writev_buffer QEMUFileOps it uses it otherwise uses
|
||||
* put_buffer ops.
|
||||
*/
|
||||
void qemu_fflush(QEMUFile *f)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (!qemu_file_is_writable(f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (f->ops->writev_buffer) {
|
||||
if (f->iovcnt > 0) {
|
||||
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
|
||||
}
|
||||
} else {
|
||||
if (f->buf_index > 0) {
|
||||
ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
|
||||
}
|
||||
}
|
||||
if (ret >= 0) {
|
||||
f->pos += ret;
|
||||
}
|
||||
f->buf_index = 0;
|
||||
f->iovcnt = 0;
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (f->ops->before_ram_iterate) {
|
||||
ret = f->ops->before_ram_iterate(f, f->opaque, flags);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (f->ops->after_ram_iterate) {
|
||||
ret = f->ops->after_ram_iterate(f, f->opaque, flags);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ram_control_load_hook(QEMUFile *f, uint64_t flags)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (f->ops->hook_ram_load) {
|
||||
ret = f->ops->hook_ram_load(f, f->opaque, flags);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
} else {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||
ram_addr_t offset, size_t size, int *bytes_sent)
|
||||
{
|
||||
if (f->ops->save_page) {
|
||||
int ret = f->ops->save_page(f, f->opaque, block_offset,
|
||||
offset, size, bytes_sent);
|
||||
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||
if (bytes_sent && *bytes_sent > 0) {
|
||||
qemu_update_position(f, *bytes_sent);
|
||||
} else if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return RAM_SAVE_CONTROL_NOT_SUPP;
|
||||
}
|
||||
|
||||
static void qemu_fill_buffer(QEMUFile *f)
|
||||
{
|
||||
int len;
|
||||
int pending;
|
||||
|
||||
assert(!qemu_file_is_writable(f));
|
||||
|
||||
pending = f->buf_size - f->buf_index;
|
||||
if (pending > 0) {
|
||||
memmove(f->buf, f->buf + f->buf_index, pending);
|
||||
}
|
||||
f->buf_index = 0;
|
||||
f->buf_size = pending;
|
||||
|
||||
len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
|
||||
IO_BUF_SIZE - pending);
|
||||
if (len > 0) {
|
||||
f->buf_size += len;
|
||||
f->pos += len;
|
||||
} else if (len == 0) {
|
||||
qemu_file_set_error(f, -EIO);
|
||||
} else if (len != -EAGAIN) {
|
||||
qemu_file_set_error(f, len);
|
||||
}
|
||||
}
|
||||
|
||||
int qemu_get_fd(QEMUFile *f)
|
||||
{
|
||||
if (f->ops->get_fd) {
|
||||
return f->ops->get_fd(f->opaque);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void qemu_update_position(QEMUFile *f, size_t size)
|
||||
{
|
||||
f->pos += size;
|
||||
}
|
||||
|
||||
/** Closes the file
|
||||
*
|
||||
* Returns negative error value if any error happened on previous operations or
|
||||
* while closing the file. Returns 0 or positive number on success.
|
||||
*
|
||||
* The meaning of return value on success depends on the specific backend
|
||||
* being used.
|
||||
*/
|
||||
int qemu_fclose(QEMUFile *f)
|
||||
{
|
||||
int ret;
|
||||
qemu_fflush(f);
|
||||
ret = qemu_file_get_error(f);
|
||||
|
||||
if (f->ops->close) {
|
||||
int ret2 = f->ops->close(f->opaque);
|
||||
if (ret >= 0) {
|
||||
ret = ret2;
|
||||
}
|
||||
}
|
||||
/* If any error was spotted before closing, we should report it
|
||||
* instead of the close() return value.
|
||||
*/
|
||||
if (f->last_error) {
|
||||
ret = f->last_error;
|
||||
}
|
||||
g_free(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
|
||||
{
|
||||
/* check for adjacent buffer and coalesce them */
|
||||
if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
|
||||
f->iov[f->iovcnt - 1].iov_len) {
|
||||
f->iov[f->iovcnt - 1].iov_len += size;
|
||||
} else {
|
||||
f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
|
||||
f->iov[f->iovcnt++].iov_len = size;
|
||||
}
|
||||
|
||||
if (f->iovcnt >= MAX_IOV_SIZE) {
|
||||
qemu_fflush(f);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
|
||||
{
|
||||
if (!f->ops->writev_buffer) {
|
||||
qemu_put_buffer(f, buf, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (f->last_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
f->bytes_xfer += size;
|
||||
add_to_iovec(f, buf, size);
|
||||
}
|
||||
|
||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
|
||||
{
|
||||
int l;
|
||||
|
||||
if (f->last_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
l = IO_BUF_SIZE - f->buf_index;
|
||||
if (l > size) {
|
||||
l = size;
|
||||
}
|
||||
memcpy(f->buf + f->buf_index, buf, l);
|
||||
f->bytes_xfer += l;
|
||||
if (f->ops->writev_buffer) {
|
||||
add_to_iovec(f, f->buf + f->buf_index, l);
|
||||
}
|
||||
f->buf_index += l;
|
||||
if (f->buf_index == IO_BUF_SIZE) {
|
||||
qemu_fflush(f);
|
||||
}
|
||||
if (qemu_file_get_error(f)) {
|
||||
break;
|
||||
}
|
||||
buf += l;
|
||||
size -= l;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_put_byte(QEMUFile *f, int v)
|
||||
{
|
||||
if (f->last_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
f->buf[f->buf_index] = v;
|
||||
f->bytes_xfer++;
|
||||
if (f->ops->writev_buffer) {
|
||||
add_to_iovec(f, f->buf + f->buf_index, 1);
|
||||
}
|
||||
f->buf_index++;
|
||||
if (f->buf_index == IO_BUF_SIZE) {
|
||||
qemu_fflush(f);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_file_skip(QEMUFile *f, int size)
|
||||
{
|
||||
if (f->buf_index + size <= f->buf_size) {
|
||||
f->buf_index += size;
|
||||
}
|
||||
}
|
||||
|
||||
int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
|
||||
{
|
||||
int pending;
|
||||
int index;
|
||||
|
||||
assert(!qemu_file_is_writable(f));
|
||||
|
||||
index = f->buf_index + offset;
|
||||
pending = f->buf_size - index;
|
||||
if (pending < size) {
|
||||
qemu_fill_buffer(f);
|
||||
index = f->buf_index + offset;
|
||||
pending = f->buf_size - index;
|
||||
}
|
||||
|
||||
if (pending <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (size > pending) {
|
||||
size = pending;
|
||||
}
|
||||
|
||||
memcpy(buf, f->buf + index, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
|
||||
{
|
||||
int pending = size;
|
||||
int done = 0;
|
||||
|
||||
while (pending > 0) {
|
||||
int res;
|
||||
|
||||
res = qemu_peek_buffer(f, buf, pending, 0);
|
||||
if (res == 0) {
|
||||
return done;
|
||||
}
|
||||
qemu_file_skip(f, res);
|
||||
buf += res;
|
||||
pending -= res;
|
||||
done += res;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int qemu_peek_byte(QEMUFile *f, int offset)
|
||||
{
|
||||
int index = f->buf_index + offset;
|
||||
|
||||
assert(!qemu_file_is_writable(f));
|
||||
|
||||
if (index >= f->buf_size) {
|
||||
qemu_fill_buffer(f);
|
||||
index = f->buf_index + offset;
|
||||
if (index >= f->buf_size) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return f->buf[index];
|
||||
}
|
||||
|
||||
int qemu_get_byte(QEMUFile *f)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = qemu_peek_byte(f, 0);
|
||||
qemu_file_skip(f, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t qemu_ftell(QEMUFile *f)
|
||||
{
|
||||
qemu_fflush(f);
|
||||
return f->pos;
|
||||
}
|
||||
|
||||
int qemu_file_rate_limit(QEMUFile *f)
|
||||
{
|
||||
if (qemu_file_get_error(f)) {
|
||||
return 1;
|
||||
}
|
||||
if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t qemu_file_get_rate_limit(QEMUFile *f)
|
||||
{
|
||||
return f->xfer_limit;
|
||||
}
|
||||
|
||||
void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
|
||||
{
|
||||
f->xfer_limit = limit;
|
||||
}
|
||||
|
||||
void qemu_file_reset_rate_limit(QEMUFile *f)
|
||||
{
|
||||
f->bytes_xfer = 0;
|
||||
}
|
||||
|
||||
void qemu_put_be16(QEMUFile *f, unsigned int v)
|
||||
{
|
||||
qemu_put_byte(f, v >> 8);
|
||||
qemu_put_byte(f, v);
|
||||
}
|
||||
|
||||
void qemu_put_be32(QEMUFile *f, unsigned int v)
|
||||
{
|
||||
qemu_put_byte(f, v >> 24);
|
||||
qemu_put_byte(f, v >> 16);
|
||||
qemu_put_byte(f, v >> 8);
|
||||
qemu_put_byte(f, v);
|
||||
}
|
||||
|
||||
void qemu_put_be64(QEMUFile *f, uint64_t v)
|
||||
{
|
||||
qemu_put_be32(f, v >> 32);
|
||||
qemu_put_be32(f, v);
|
||||
}
|
||||
|
||||
unsigned int qemu_get_be16(QEMUFile *f)
|
||||
{
|
||||
unsigned int v;
|
||||
v = qemu_get_byte(f) << 8;
|
||||
v |= qemu_get_byte(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned int qemu_get_be32(QEMUFile *f)
|
||||
{
|
||||
unsigned int v;
|
||||
v = qemu_get_byte(f) << 24;
|
||||
v |= qemu_get_byte(f) << 16;
|
||||
v |= qemu_get_byte(f) << 8;
|
||||
v |= qemu_get_byte(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
uint64_t qemu_get_be64(QEMUFile *f)
|
||||
{
|
||||
uint64_t v;
|
||||
v = (uint64_t)qemu_get_be32(f) << 32;
|
||||
v |= qemu_get_be32(f);
|
||||
return v;
|
||||
}
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
|
@ -20,6 +20,7 @@ test-qmp-commands
|
|||
test-qmp-input-strict
|
||||
test-qmp-marshal.c
|
||||
test-thread-pool
|
||||
test-vmstate
|
||||
test-x86-cpuid
|
||||
test-xbzrle
|
||||
*-test
|
||||
|
|
|
@ -54,6 +54,7 @@ check-unit-y += tests/test-bitops$(EXESUF)
|
|||
check-unit-y += tests/test-qdev-global-props$(EXESUF)
|
||||
check-unit-y += tests/check-qom-interface$(EXESUF)
|
||||
gcov-files-check-qom-interface-y = qom/object.c
|
||||
check-unit-y += tests/test-vmstate$(EXESUF)
|
||||
|
||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||
|
||||
|
@ -167,6 +168,9 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
|||
$(qom-core-obj) \
|
||||
$(test-qapi-obj-y) \
|
||||
libqemuutil.a libqemustub.a
|
||||
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
||||
vmstate.o qemu-file.o \
|
||||
libqemuutil.a
|
||||
|
||||
tests/test-qapi-types.c tests/test-qapi-types.h :\
|
||||
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
|
||||
|
|
357
tests/test-vmstate.c
Normal file
357
tests/test-vmstate.c
Normal file
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* Test code for VMState
|
||||
*
|
||||
* Copyright (c) 2013 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "block/coroutine.h"
|
||||
|
||||
char temp_file[] = "/tmp/vmst.test.XXXXXX";
|
||||
int temp_fd;
|
||||
|
||||
/* Fake yield_until_fd_readable() implementation so we don't have to pull the
|
||||
* coroutine code as dependency.
|
||||
*/
|
||||
void yield_until_fd_readable(int fd)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
select(fd + 1, &fds, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Duplicate temp_fd and seek to the beginning of the file */
|
||||
static int dup_temp_fd(bool truncate)
|
||||
{
|
||||
int fd = dup(temp_fd);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
if (truncate) {
|
||||
g_assert_cmpint(ftruncate(fd, 0), ==, 0);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
typedef struct TestSruct {
|
||||
uint32_t a, b, c, e;
|
||||
uint64_t d, f;
|
||||
bool skip_c_e;
|
||||
} TestStruct;
|
||||
|
||||
|
||||
static const VMStateDescription vmstate_simple = {
|
||||
.name = "test",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(a, TestStruct),
|
||||
VMSTATE_UINT32(b, TestStruct),
|
||||
VMSTATE_UINT32(c, TestStruct),
|
||||
VMSTATE_UINT64(d, TestStruct),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void test_simple_save(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 };
|
||||
vmstate_save_state(fsave, &vmstate_simple, &obj);
|
||||
g_assert(!qemu_file_get_error(fsave));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
uint8_t expected[] = {
|
||||
0, 0, 0, 1, /* a */
|
||||
0, 0, 0, 2, /* b */
|
||||
0, 0, 0, 3, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
||||
};
|
||||
uint8_t result[sizeof(expected)];
|
||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
||||
sizeof(result));
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
|
||||
|
||||
/* Must reach EOF */
|
||||
qemu_get_byte(loading);
|
||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
||||
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static void test_simple_load(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
0, 0, 0, 30, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 40, /* d */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
TestStruct obj;
|
||||
vmstate_load_state(loading, &vmstate_simple, &obj, 1);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(obj.a, ==, 10);
|
||||
g_assert_cmpint(obj.b, ==, 20);
|
||||
g_assert_cmpint(obj.c, ==, 30);
|
||||
g_assert_cmpint(obj.d, ==, 40);
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_versioned = {
|
||||
.name = "test",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT32(a, TestStruct),
|
||||
VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so
|
||||
* we catch bugs more easily.
|
||||
*/
|
||||
VMSTATE_UINT32(c, TestStruct),
|
||||
VMSTATE_UINT64(d, TestStruct),
|
||||
VMSTATE_UINT32_V(e, TestStruct, 2),
|
||||
VMSTATE_UINT64_V(f, TestStruct, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void test_load_v1(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 30, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 40, /* d */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
TestStruct obj = { .b = 200, .e = 500, .f = 600 };
|
||||
vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(obj.a, ==, 10);
|
||||
g_assert_cmpint(obj.b, ==, 200);
|
||||
g_assert_cmpint(obj.c, ==, 30);
|
||||
g_assert_cmpint(obj.d, ==, 40);
|
||||
g_assert_cmpint(obj.e, ==, 500);
|
||||
g_assert_cmpint(obj.f, ==, 600);
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static void test_load_v2(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
0, 0, 0, 30, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 40, /* d */
|
||||
0, 0, 0, 50, /* e */
|
||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
TestStruct obj;
|
||||
vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
|
||||
g_assert_cmpint(obj.a, ==, 10);
|
||||
g_assert_cmpint(obj.b, ==, 20);
|
||||
g_assert_cmpint(obj.c, ==, 30);
|
||||
g_assert_cmpint(obj.d, ==, 40);
|
||||
g_assert_cmpint(obj.e, ==, 50);
|
||||
g_assert_cmpint(obj.f, ==, 60);
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static bool test_skip(void *opaque, int version_id)
|
||||
{
|
||||
TestStruct *t = (TestStruct *)opaque;
|
||||
return !t->skip_c_e;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_skipping = {
|
||||
.name = "test",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT32(a, TestStruct),
|
||||
VMSTATE_UINT32(b, TestStruct),
|
||||
VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
|
||||
VMSTATE_UINT64(d, TestStruct),
|
||||
VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
|
||||
VMSTATE_UINT64_V(f, TestStruct, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void test_save_noskip(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||
.skip_c_e = false };
|
||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||
g_assert(!qemu_file_get_error(fsave));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
uint8_t expected[] = {
|
||||
0, 0, 0, 1, /* a */
|
||||
0, 0, 0, 2, /* b */
|
||||
0, 0, 0, 3, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
||||
0, 0, 0, 5, /* e */
|
||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||
};
|
||||
uint8_t result[sizeof(expected)];
|
||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
||||
sizeof(result));
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
|
||||
|
||||
/* Must reach EOF */
|
||||
qemu_get_byte(loading);
|
||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
||||
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static void test_save_skip(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||
.skip_c_e = true };
|
||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||
g_assert(!qemu_file_get_error(fsave));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
uint8_t expected[] = {
|
||||
0, 0, 0, 1, /* a */
|
||||
0, 0, 0, 2, /* b */
|
||||
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||
};
|
||||
uint8_t result[sizeof(expected)];
|
||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
||||
sizeof(result));
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
|
||||
|
||||
|
||||
/* Must reach EOF */
|
||||
qemu_get_byte(loading);
|
||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
||||
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static void test_load_noskip(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
0, 0, 0, 30, /* c */
|
||||
0, 0, 0, 0, 0, 0, 0, 40, /* d */
|
||||
0, 0, 0, 50, /* e */
|
||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
TestStruct obj = { .skip_c_e = false };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(obj.a, ==, 10);
|
||||
g_assert_cmpint(obj.b, ==, 20);
|
||||
g_assert_cmpint(obj.c, ==, 30);
|
||||
g_assert_cmpint(obj.d, ==, 40);
|
||||
g_assert_cmpint(obj.e, ==, 50);
|
||||
g_assert_cmpint(obj.f, ==, 60);
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
static void test_load_skip(void)
|
||||
{
|
||||
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 10, /* a */
|
||||
0, 0, 0, 20, /* b */
|
||||
0, 0, 0, 0, 0, 0, 0, 40, /* d */
|
||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||
};
|
||||
qemu_put_buffer(fsave, buf, sizeof(buf));
|
||||
qemu_fclose(fsave);
|
||||
|
||||
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
|
||||
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
|
||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||
g_assert(!qemu_file_get_error(loading));
|
||||
g_assert_cmpint(obj.a, ==, 10);
|
||||
g_assert_cmpint(obj.b, ==, 20);
|
||||
g_assert_cmpint(obj.c, ==, 300);
|
||||
g_assert_cmpint(obj.d, ==, 40);
|
||||
g_assert_cmpint(obj.e, ==, 500);
|
||||
g_assert_cmpint(obj.f, ==, 60);
|
||||
qemu_fclose(loading);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
temp_fd = mkstemp(temp_file);
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/vmstate/simple/save", test_simple_save);
|
||||
g_test_add_func("/vmstate/simple/load", test_simple_load);
|
||||
g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
|
||||
g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
|
||||
g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
|
||||
g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
|
||||
g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
|
||||
g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
|
||||
g_test_run();
|
||||
|
||||
close(temp_fd);
|
||||
unlink(temp_file);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -36,9 +36,9 @@
|
|||
* endian architectures.
|
||||
*/
|
||||
|
||||
int slow_bitmap_empty(const unsigned long *bitmap, int bits)
|
||||
int slow_bitmap_empty(const unsigned long *bitmap, long bits)
|
||||
{
|
||||
int k, lim = bits/BITS_PER_LONG;
|
||||
long k, lim = bits/BITS_PER_LONG;
|
||||
|
||||
for (k = 0; k < lim; ++k) {
|
||||
if (bitmap[k]) {
|
||||
|
@ -54,9 +54,9 @@ int slow_bitmap_empty(const unsigned long *bitmap, int bits)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int slow_bitmap_full(const unsigned long *bitmap, int bits)
|
||||
int slow_bitmap_full(const unsigned long *bitmap, long bits)
|
||||
{
|
||||
int k, lim = bits/BITS_PER_LONG;
|
||||
long k, lim = bits/BITS_PER_LONG;
|
||||
|
||||
for (k = 0; k < lim; ++k) {
|
||||
if (~bitmap[k]) {
|
||||
|
@ -74,9 +74,9 @@ int slow_bitmap_full(const unsigned long *bitmap, int bits)
|
|||
}
|
||||
|
||||
int slow_bitmap_equal(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k, lim = bits/BITS_PER_LONG;
|
||||
long k, lim = bits/BITS_PER_LONG;
|
||||
|
||||
for (k = 0; k < lim; ++k) {
|
||||
if (bitmap1[k] != bitmap2[k]) {
|
||||
|
@ -94,9 +94,9 @@ int slow_bitmap_equal(const unsigned long *bitmap1,
|
|||
}
|
||||
|
||||
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
|
||||
int bits)
|
||||
long bits)
|
||||
{
|
||||
int k, lim = bits/BITS_PER_LONG;
|
||||
long k, lim = bits/BITS_PER_LONG;
|
||||
|
||||
for (k = 0; k < lim; ++k) {
|
||||
dst[k] = ~src[k];
|
||||
|
@ -108,10 +108,10 @@ void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
|
|||
}
|
||||
|
||||
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(bits);
|
||||
long k;
|
||||
long nr = BITS_TO_LONGS(bits);
|
||||
unsigned long result = 0;
|
||||
|
||||
for (k = 0; k < nr; k++) {
|
||||
|
@ -121,10 +121,10 @@ int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
|
|||
}
|
||||
|
||||
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(bits);
|
||||
long k;
|
||||
long nr = BITS_TO_LONGS(bits);
|
||||
|
||||
for (k = 0; k < nr; k++) {
|
||||
dst[k] = bitmap1[k] | bitmap2[k];
|
||||
|
@ -132,10 +132,10 @@ void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
|
|||
}
|
||||
|
||||
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(bits);
|
||||
long k;
|
||||
long nr = BITS_TO_LONGS(bits);
|
||||
|
||||
for (k = 0; k < nr; k++) {
|
||||
dst[k] = bitmap1[k] ^ bitmap2[k];
|
||||
|
@ -143,10 +143,10 @@ void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
|
|||
}
|
||||
|
||||
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k;
|
||||
int nr = BITS_TO_LONGS(bits);
|
||||
long k;
|
||||
long nr = BITS_TO_LONGS(bits);
|
||||
unsigned long result = 0;
|
||||
|
||||
for (k = 0; k < nr; k++) {
|
||||
|
@ -157,10 +157,10 @@ int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
|||
|
||||
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
|
||||
|
||||
void bitmap_set(unsigned long *map, int start, int nr)
|
||||
void bitmap_set(unsigned long *map, long start, long nr)
|
||||
{
|
||||
unsigned long *p = map + BIT_WORD(start);
|
||||
const int size = start + nr;
|
||||
const long size = start + nr;
|
||||
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
|
||||
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
|
||||
|
||||
|
@ -177,10 +177,10 @@ void bitmap_set(unsigned long *map, int start, int nr)
|
|||
}
|
||||
}
|
||||
|
||||
void bitmap_clear(unsigned long *map, int start, int nr)
|
||||
void bitmap_clear(unsigned long *map, long start, long nr)
|
||||
{
|
||||
unsigned long *p = map + BIT_WORD(start);
|
||||
const int size = start + nr;
|
||||
const long size = start + nr;
|
||||
int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
|
||||
unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
|
||||
|
||||
|
@ -214,7 +214,7 @@ void bitmap_clear(unsigned long *map, int start, int nr)
|
|||
unsigned long bitmap_find_next_zero_area(unsigned long *map,
|
||||
unsigned long size,
|
||||
unsigned long start,
|
||||
unsigned int nr,
|
||||
unsigned long nr,
|
||||
unsigned long align_mask)
|
||||
{
|
||||
unsigned long index, end, i;
|
||||
|
@ -237,9 +237,9 @@ again:
|
|||
}
|
||||
|
||||
int slow_bitmap_intersects(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, int bits)
|
||||
const unsigned long *bitmap2, long bits)
|
||||
{
|
||||
int k, lim = bits/BITS_PER_LONG;
|
||||
long k, lim = bits/BITS_PER_LONG;
|
||||
|
||||
for (k = 0; k < lim; ++k) {
|
||||
if (bitmap1[k] & bitmap2[k]) {
|
||||
|
|
650
vmstate.c
Normal file
650
vmstate.c
Normal file
|
@ -0,0 +1,650 @@
|
|||
#include "qemu-common.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/qemu-file.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque);
|
||||
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque);
|
||||
|
||||
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque, int version_id)
|
||||
{
|
||||
VMStateField *field = vmsd->fields;
|
||||
int ret;
|
||||
|
||||
if (version_id > vmsd->version_id) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (version_id < vmsd->minimum_version_id_old) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (version_id < vmsd->minimum_version_id) {
|
||||
return vmsd->load_state_old(f, opaque, version_id);
|
||||
}
|
||||
if (vmsd->pre_load) {
|
||||
int ret = vmsd->pre_load(opaque);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
while (field->name) {
|
||||
if ((field->field_exists &&
|
||||
field->field_exists(opaque, version_id)) ||
|
||||
(!field->field_exists &&
|
||||
field->version_id <= version_id)) {
|
||||
void *base_addr = opaque + field->offset;
|
||||
int i, n_elems = 1;
|
||||
int size = field->size;
|
||||
|
||||
if (field->flags & VMS_VBUFFER) {
|
||||
size = *(int32_t *)(opaque+field->size_offset);
|
||||
if (field->flags & VMS_MULTIPLY) {
|
||||
size *= field->size;
|
||||
}
|
||||
}
|
||||
if (field->flags & VMS_ARRAY) {
|
||||
n_elems = field->num;
|
||||
} else if (field->flags & VMS_VARRAY_INT32) {
|
||||
n_elems = *(int32_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT32) {
|
||||
n_elems = *(uint32_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT16) {
|
||||
n_elems = *(uint16_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT8) {
|
||||
n_elems = *(uint8_t *)(opaque+field->num_offset);
|
||||
}
|
||||
if (field->flags & VMS_POINTER) {
|
||||
base_addr = *(void **)base_addr + field->start;
|
||||
}
|
||||
for (i = 0; i < n_elems; i++) {
|
||||
void *addr = base_addr + size * i;
|
||||
|
||||
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
||||
addr = *(void **)addr;
|
||||
}
|
||||
if (field->flags & VMS_STRUCT) {
|
||||
ret = vmstate_load_state(f, field->vmsd, addr,
|
||||
field->vmsd->version_id);
|
||||
} else {
|
||||
ret = field->info->get(f, addr, size);
|
||||
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
field++;
|
||||
}
|
||||
ret = vmstate_subsection_load(f, vmsd, opaque);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (vmsd->post_load) {
|
||||
return vmsd->post_load(opaque, version_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque)
|
||||
{
|
||||
VMStateField *field = vmsd->fields;
|
||||
|
||||
if (vmsd->pre_save) {
|
||||
vmsd->pre_save(opaque);
|
||||
}
|
||||
while (field->name) {
|
||||
if (!field->field_exists ||
|
||||
field->field_exists(opaque, vmsd->version_id)) {
|
||||
void *base_addr = opaque + field->offset;
|
||||
int i, n_elems = 1;
|
||||
int size = field->size;
|
||||
|
||||
if (field->flags & VMS_VBUFFER) {
|
||||
size = *(int32_t *)(opaque+field->size_offset);
|
||||
if (field->flags & VMS_MULTIPLY) {
|
||||
size *= field->size;
|
||||
}
|
||||
}
|
||||
if (field->flags & VMS_ARRAY) {
|
||||
n_elems = field->num;
|
||||
} else if (field->flags & VMS_VARRAY_INT32) {
|
||||
n_elems = *(int32_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT32) {
|
||||
n_elems = *(uint32_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT16) {
|
||||
n_elems = *(uint16_t *)(opaque+field->num_offset);
|
||||
} else if (field->flags & VMS_VARRAY_UINT8) {
|
||||
n_elems = *(uint8_t *)(opaque+field->num_offset);
|
||||
}
|
||||
if (field->flags & VMS_POINTER) {
|
||||
base_addr = *(void **)base_addr + field->start;
|
||||
}
|
||||
for (i = 0; i < n_elems; i++) {
|
||||
void *addr = base_addr + size * i;
|
||||
|
||||
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
||||
addr = *(void **)addr;
|
||||
}
|
||||
if (field->flags & VMS_STRUCT) {
|
||||
vmstate_save_state(f, field->vmsd, addr);
|
||||
} else {
|
||||
field->info->put(f, addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
field++;
|
||||
}
|
||||
vmstate_subsection_save(f, vmsd, opaque);
|
||||
}
|
||||
|
||||
static const VMStateDescription *
|
||||
vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
|
||||
{
|
||||
while (sub && sub->needed) {
|
||||
if (strcmp(idstr, sub->vmsd->name) == 0) {
|
||||
return sub->vmsd;
|
||||
}
|
||||
sub++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque)
|
||||
{
|
||||
while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
|
||||
char idstr[256];
|
||||
int ret;
|
||||
uint8_t version_id, len, size;
|
||||
const VMStateDescription *sub_vmsd;
|
||||
|
||||
len = qemu_peek_byte(f, 1);
|
||||
if (len < strlen(vmsd->name) + 1) {
|
||||
/* subsection name has be be "section_name/a" */
|
||||
return 0;
|
||||
}
|
||||
size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
|
||||
if (size != len) {
|
||||
return 0;
|
||||
}
|
||||
idstr[size] = 0;
|
||||
|
||||
if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
|
||||
/* it don't have a valid subsection name */
|
||||
return 0;
|
||||
}
|
||||
sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
|
||||
if (sub_vmsd == NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
qemu_file_skip(f, 1); /* subsection */
|
||||
qemu_file_skip(f, 1); /* len */
|
||||
qemu_file_skip(f, len); /* idstr */
|
||||
version_id = qemu_get_be32(f);
|
||||
|
||||
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque)
|
||||
{
|
||||
const VMStateSubsection *sub = vmsd->subsections;
|
||||
|
||||
while (sub && sub->needed) {
|
||||
if (sub->needed(opaque)) {
|
||||
const VMStateDescription *vmsd = sub->vmsd;
|
||||
uint8_t len;
|
||||
|
||||
qemu_put_byte(f, QEMU_VM_SUBSECTION);
|
||||
len = strlen(vmsd->name);
|
||||
qemu_put_byte(f, len);
|
||||
qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
|
||||
qemu_put_be32(f, vmsd->version_id);
|
||||
vmstate_save_state(f, vmsd, opaque);
|
||||
}
|
||||
sub++;
|
||||
}
|
||||
}
|
||||
|
||||
/* bool */
|
||||
|
||||
static int get_bool(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
bool *v = pv;
|
||||
*v = qemu_get_byte(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_bool(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
bool *v = pv;
|
||||
qemu_put_byte(f, *v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_bool = {
|
||||
.name = "bool",
|
||||
.get = get_bool,
|
||||
.put = put_bool,
|
||||
};
|
||||
|
||||
/* 8 bit int */
|
||||
|
||||
static int get_int8(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int8_t *v = pv;
|
||||
qemu_get_s8s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_int8(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int8_t *v = pv;
|
||||
qemu_put_s8s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int8 = {
|
||||
.name = "int8",
|
||||
.get = get_int8,
|
||||
.put = put_int8,
|
||||
};
|
||||
|
||||
/* 16 bit int */
|
||||
|
||||
static int get_int16(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int16_t *v = pv;
|
||||
qemu_get_sbe16s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_int16(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int16_t *v = pv;
|
||||
qemu_put_sbe16s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int16 = {
|
||||
.name = "int16",
|
||||
.get = get_int16,
|
||||
.put = put_int16,
|
||||
};
|
||||
|
||||
/* 32 bit int */
|
||||
|
||||
static int get_int32(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int32_t *v = pv;
|
||||
qemu_get_sbe32s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_int32(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int32_t *v = pv;
|
||||
qemu_put_sbe32s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int32 = {
|
||||
.name = "int32",
|
||||
.get = get_int32,
|
||||
.put = put_int32,
|
||||
};
|
||||
|
||||
/* 32 bit int. See that the received value is the same than the one
|
||||
in the field */
|
||||
|
||||
static int get_int32_equal(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int32_t *v = pv;
|
||||
int32_t v2;
|
||||
qemu_get_sbe32s(f, &v2);
|
||||
|
||||
if (*v == v2) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int32_equal = {
|
||||
.name = "int32 equal",
|
||||
.get = get_int32_equal,
|
||||
.put = put_int32,
|
||||
};
|
||||
|
||||
/* 32 bit int. See that the received value is the less or the same
|
||||
than the one in the field */
|
||||
|
||||
static int get_int32_le(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int32_t *old = pv;
|
||||
int32_t new;
|
||||
qemu_get_sbe32s(f, &new);
|
||||
|
||||
if (*old <= new) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int32_le = {
|
||||
.name = "int32 equal",
|
||||
.get = get_int32_le,
|
||||
.put = put_int32,
|
||||
};
|
||||
|
||||
/* 64 bit int */
|
||||
|
||||
static int get_int64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int64_t *v = pv;
|
||||
qemu_get_sbe64s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_int64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
int64_t *v = pv;
|
||||
qemu_put_sbe64s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_int64 = {
|
||||
.name = "int64",
|
||||
.get = get_int64,
|
||||
.put = put_int64,
|
||||
};
|
||||
|
||||
/* 8 bit unsigned int */
|
||||
|
||||
static int get_uint8(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t *v = pv;
|
||||
qemu_get_8s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_uint8(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t *v = pv;
|
||||
qemu_put_8s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint8 = {
|
||||
.name = "uint8",
|
||||
.get = get_uint8,
|
||||
.put = put_uint8,
|
||||
};
|
||||
|
||||
/* 16 bit unsigned int */
|
||||
|
||||
static int get_uint16(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint16_t *v = pv;
|
||||
qemu_get_be16s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_uint16(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint16_t *v = pv;
|
||||
qemu_put_be16s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint16 = {
|
||||
.name = "uint16",
|
||||
.get = get_uint16,
|
||||
.put = put_uint16,
|
||||
};
|
||||
|
||||
/* 32 bit unsigned int */
|
||||
|
||||
static int get_uint32(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint32_t *v = pv;
|
||||
qemu_get_be32s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_uint32(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint32_t *v = pv;
|
||||
qemu_put_be32s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint32 = {
|
||||
.name = "uint32",
|
||||
.get = get_uint32,
|
||||
.put = put_uint32,
|
||||
};
|
||||
|
||||
/* 32 bit uint. See that the received value is the same than the one
|
||||
in the field */
|
||||
|
||||
static int get_uint32_equal(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint32_t *v = pv;
|
||||
uint32_t v2;
|
||||
qemu_get_be32s(f, &v2);
|
||||
|
||||
if (*v == v2) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint32_equal = {
|
||||
.name = "uint32 equal",
|
||||
.get = get_uint32_equal,
|
||||
.put = put_uint32,
|
||||
};
|
||||
|
||||
/* 64 bit unsigned int */
|
||||
|
||||
static int get_uint64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
qemu_get_be64s(f, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_uint64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
qemu_put_be64s(f, v);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint64 = {
|
||||
.name = "uint64",
|
||||
.get = get_uint64,
|
||||
.put = put_uint64,
|
||||
};
|
||||
|
||||
/* 64 bit unsigned int. See that the received value is the same than the one
|
||||
in the field */
|
||||
|
||||
static int get_uint64_equal(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
uint64_t v2;
|
||||
qemu_get_be64s(f, &v2);
|
||||
|
||||
if (*v == v2) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint64_equal = {
|
||||
.name = "int64 equal",
|
||||
.get = get_uint64_equal,
|
||||
.put = put_uint64,
|
||||
};
|
||||
|
||||
/* 8 bit int. See that the received value is the same than the one
|
||||
in the field */
|
||||
|
||||
static int get_uint8_equal(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t *v = pv;
|
||||
uint8_t v2;
|
||||
qemu_get_8s(f, &v2);
|
||||
|
||||
if (*v == v2) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint8_equal = {
|
||||
.name = "uint8 equal",
|
||||
.get = get_uint8_equal,
|
||||
.put = put_uint8,
|
||||
};
|
||||
|
||||
/* 16 bit unsigned int int. See that the received value is the same than the one
|
||||
in the field */
|
||||
|
||||
static int get_uint16_equal(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint16_t *v = pv;
|
||||
uint16_t v2;
|
||||
qemu_get_be16s(f, &v2);
|
||||
|
||||
if (*v == v2) {
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_uint16_equal = {
|
||||
.name = "uint16 equal",
|
||||
.get = get_uint16_equal,
|
||||
.put = put_uint16,
|
||||
};
|
||||
|
||||
/* floating point */
|
||||
|
||||
static int get_float64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
float64 *v = pv;
|
||||
|
||||
*v = make_float64(qemu_get_be64(f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_float64(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
|
||||
qemu_put_be64(f, float64_val(*v));
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_float64 = {
|
||||
.name = "float64",
|
||||
.get = get_float64,
|
||||
.put = put_float64,
|
||||
};
|
||||
|
||||
/* uint8_t buffers */
|
||||
|
||||
static int get_buffer(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t *v = pv;
|
||||
qemu_get_buffer(f, v, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_buffer(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t *v = pv;
|
||||
qemu_put_buffer(f, v, size);
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_buffer = {
|
||||
.name = "buffer",
|
||||
.get = get_buffer,
|
||||
.put = put_buffer,
|
||||
};
|
||||
|
||||
/* unused buffers: space that was used for some fields that are
|
||||
not useful anymore */
|
||||
|
||||
static int get_unused_buffer(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
int block_len;
|
||||
|
||||
while (size > 0) {
|
||||
block_len = MIN(sizeof(buf), size);
|
||||
size -= block_len;
|
||||
qemu_get_buffer(f, buf, block_len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_unused_buffer(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
static const uint8_t buf[1024];
|
||||
int block_len;
|
||||
|
||||
while (size > 0) {
|
||||
block_len = MIN(sizeof(buf), size);
|
||||
size -= block_len;
|
||||
qemu_put_buffer(f, buf, block_len);
|
||||
}
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_unused_buffer = {
|
||||
.name = "unused_buffer",
|
||||
.get = get_unused_buffer,
|
||||
.put = put_unused_buffer,
|
||||
};
|
||||
|
||||
/* bitmaps (as defined by bitmap.h). Note that size here is the size
|
||||
* of the bitmap in bits. The on-the-wire format of a bitmap is 64
|
||||
* bit words with the bits in big endian order. The in-memory format
|
||||
* is an array of 'unsigned long', which may be either 32 or 64 bits.
|
||||
*/
|
||||
/* This is the number of 64 bit words sent over the wire */
|
||||
#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64)
|
||||
static int get_bitmap(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
unsigned long *bmp = pv;
|
||||
int i, idx = 0;
|
||||
for (i = 0; i < BITS_TO_U64S(size); i++) {
|
||||
uint64_t w = qemu_get_be64(f);
|
||||
bmp[idx++] = w;
|
||||
if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
|
||||
bmp[idx++] = w >> 32;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_bitmap(QEMUFile *f, void *pv, size_t size)
|
||||
{
|
||||
unsigned long *bmp = pv;
|
||||
int i, idx = 0;
|
||||
for (i = 0; i < BITS_TO_U64S(size); i++) {
|
||||
uint64_t w = bmp[idx++];
|
||||
if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
|
||||
w |= ((uint64_t)bmp[idx++]) << 32;
|
||||
}
|
||||
qemu_put_be64(f, w);
|
||||
}
|
||||
}
|
||||
|
||||
const VMStateInfo vmstate_info_bitmap = {
|
||||
.name = "bitmap",
|
||||
.get = get_bitmap,
|
||||
.put = put_bitmap,
|
||||
};
|
Loading…
Reference in a new issue