diff --git a/Makefile.objs b/Makefile.objs index 2b6c1fe2a8..857bb53ae4 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 diff --git a/arch_init.c b/arch_init.c index e0acbc5661..77912e7a7d 100644 --- a/arch_init.c +++ b/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); diff --git a/cputlb.c b/cputlb.c index 9270055b83..b533f3f372 100644 --- a/cputlb.c +++ b/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; diff --git a/exec.c b/exec.c index 7e49e8e555..b387d2856d 100644 --- a/exec.c +++ b/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); } } } diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index b6998f055a..4cb4b4a53a 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -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; diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index d0e063392a..25c43c06e9 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -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 diff --git a/include/exec/memory.h b/include/exec/memory.h index 480dfbf9da..296d6ab2f4 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -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 #include #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]; diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h new file mode 100644 index 0000000000..33c8acc02e --- /dev/null +++ b/include/exec/ram_addr.h @@ -0,0 +1,147 @@ +/* + * Declarations for cpu physical memory functions + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * 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 diff --git a/include/migration/migration.h b/include/migration/migration.h index 140e6b471c..bfa3951a61 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -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; diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 0f757fbeb6..a191fb6d8d 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -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) diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 308bbb71e9..1babd5d812 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -31,7 +31,7 @@ * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) * bitmap_complement(dst, src, nbits) *dst = ~(*src) * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? - * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? + * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? * bitmap_empty(src, nbits) Are all bits zero in *src? * bitmap_full(src, nbits) Are all bits set in *src? * bitmap_set(dst, pos, nbits) Set specified bit area @@ -62,71 +62,71 @@ ) #define DECLARE_BITMAP(name,bits) \ - unsigned long name[BITS_TO_LONGS(bits)] + unsigned long name[BITS_TO_LONGS(bits)] #define small_nbits(nbits) \ - ((nbits) <= BITS_PER_LONG) + ((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 align_mask); + unsigned long size, + unsigned long start, + 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 */ diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 304c90c2b4..340b1e73bd 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.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))); } diff --git a/kvm-all.c b/kvm-all.c index 393775459d..0bfb060fa7 100644 --- a/kvm-all.c +++ b/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; } diff --git a/memory.c b/memory.c index 776431416f..59ecc28401 100644 --- a/memory.c +++ b/memory.c @@ -22,6 +22,7 @@ #include #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) diff --git a/migration.c b/migration.c index 2b1ab20c54..e5f6b98023 100644 --- a/migration.c +++ b/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(); diff --git a/qemu-file.c b/qemu-file.c new file mode 100644 index 0000000000..9473b674ba --- /dev/null +++ b/qemu-file.c @@ -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; +} diff --git a/savevm.c b/savevm.c index 3f912ddcf9..a7dbe18a67 100644 --- a/savevm.c +++ b/savevm.c @@ -38,7 +38,6 @@ #include "exec/memory.h" #include "qmp-commands.h" #include "trace.h" -#include "qemu/bitops.h" #include "qemu/iov.h" #include "block/snapshot.h" #include "block/qapi.h" @@ -53,7 +52,7 @@ #define ARP_OP_REQUEST_REV 0x3 static int announce_self_create(uint8_t *buf, - uint8_t *mac_addr) + uint8_t *mac_addr) { /* Ethernet header. */ memset(buf, 0xff, 6); /* destination MAC addr */ @@ -100,411 +99,21 @@ static void qemu_announce_self_once(void *opaque) timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { - timer_del(timer); - timer_free(timer); + timer_del(timer); + timer_free(timer); } } void qemu_announce_self(void) { - static QEMUTimer *timer; - timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); - qemu_announce_self_once(&timer); + static QEMUTimer *timer; + timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); + qemu_announce_self_once(&timer); } /***********************************************************/ /* savevm/loadvm support */ -#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; -} - static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, int64_t pos) { @@ -550,441 +159,16 @@ static const QEMUFileOps bdrv_write_ops = { static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { - if (is_writable) + if (is_writable) { return qemu_fopen_ops(bs, &bdrv_write_ops); + } return qemu_fopen_ops(bs, &bdrv_read_ops); } -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. - * +/* QEMUFile timer support. + * Not in qemu-file.c to not add qemu-timer.c as dependency to qemu-file.c */ -int qemu_file_get_error(QEMUFile *f) -{ - return f->last_error; -} - -static 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); - } -} - -static void qemu_file_skip(QEMUFile *f, int size) -{ - if (f->buf_index + size <= f->buf_size) { - f->buf_index += size; - } -} - -static 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; -} - -static 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; -} - - -/* timer */ void timer_put(QEMUFile *f, QEMUTimer *ts) { @@ -1007,341 +191,9 @@ void timer_get(QEMUFile *f, QEMUTimer *ts) } -/* 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, -}; - -/* timers */ +/* VMState timer support. + * Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c + */ static int get_timer(QEMUFile *f, void *pv, size_t size) { @@ -1362,100 +214,6 @@ const VMStateInfo vmstate_info_timer = { .put = put_timer, }; -/* 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, -}; typedef struct CompatEntry { char idstr[256]; @@ -1502,8 +260,9 @@ static int calculate_compat_instance_id(const char *idstr) int instance_id = 0; QTAILQ_FOREACH(se, &savevm_handlers, entry) { - if (!se->compat) + if (!se->compat) { continue; + } if (strcmp(idstr, se->compat->idstr) == 0 && instance_id <= se->compat->instance_id) { @@ -1668,142 +427,6 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, } } -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 int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) { if (!se->vmsd) { /* Old style */ @@ -1818,20 +441,9 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se) se->ops->save_state(f, se->opaque); return; } - vmstate_save_state(f,se->vmsd, se->opaque); + vmstate_save_state(f, se->vmsd, se->opaque); } -#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 - bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -1857,7 +469,7 @@ void qemu_savevm_state_begin(QEMUFile *f, } se->ops->set_params(params, se->opaque); } - + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); @@ -1970,7 +582,7 @@ void qemu_savevm_state_complete(QEMUFile *f) int len; if ((!se->ops || !se->ops->save_state) && !se->vmsd) { - continue; + continue; } trace_savevm_section_start(); /* Section type */ @@ -2115,79 +727,6 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) return NULL; } -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++; - } -} - typedef struct LoadStateEntry { QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; @@ -2209,16 +748,18 @@ int qemu_loadvm_state(QEMUFile *f) } v = qemu_get_be32(f); - if (v != QEMU_VM_FILE_MAGIC) + if (v != QEMU_VM_FILE_MAGIC) { return -EINVAL; + } v = qemu_get_be32(f); if (v == QEMU_VM_FILE_VERSION_COMPAT) { fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n"); return -ENOTSUP; } - if (v != QEMU_VM_FILE_VERSION) + if (v != QEMU_VM_FILE_VERSION) { return -ENOTSUP; + } while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { uint32_t instance_id, version_id, section_id; @@ -2337,8 +878,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name) bs = NULL; while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs) && - bdrv_snapshot_find(bs, snapshot, name) >= 0) - { + bdrv_snapshot_find(bs, snapshot, name) >= 0) { bdrv_snapshot_delete_by_id_or_name(bs, name, &err); if (error_is_set(&err)) { monitor_printf(mon, @@ -2448,8 +988,9 @@ void do_savevm(Monitor *mon, const QDict *qdict) } the_end: - if (saved_vm_running) + if (saved_vm_running) { vm_start(); + } } void qmp_xen_save_devices_state(const char *filename, Error **errp) @@ -2473,8 +1014,9 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp) } the_end: - if (saved_vm_running) + if (saved_vm_running) { vm_start(); + } } int load_vmstate(const char *name) diff --git a/tests/.gitignore b/tests/.gitignore index 425757cfe1..1aed2249ff 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -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 diff --git a/tests/Makefile b/tests/Makefile index 0b85a34147..0aaf657be5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c new file mode 100644 index 0000000000..75cd1a1fd4 --- /dev/null +++ b/tests/test-vmstate.c @@ -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 + +#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; +} diff --git a/util/bitmap.c b/util/bitmap.c index 687841dcec..9c6bb526f6 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -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); @@ -212,10 +212,10 @@ void bitmap_clear(unsigned long *map, int start, int nr) * power of 2. A @align_mask of 0 means no alignment is required. */ unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, - unsigned int nr, - unsigned long align_mask) + unsigned long size, + unsigned long start, + unsigned long nr, + unsigned long align_mask) { unsigned long index, end, i; again: @@ -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]) { diff --git a/vmstate.c b/vmstate.c new file mode 100644 index 0000000000..284b080f46 --- /dev/null +++ b/vmstate.c @@ -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, +};