Merge remote-tracking branch 'sstabellini/saverestore-8' into staging

* sstabellini/saverestore-8:
  xen: do not allocate RAM during INMIGRATE runstate
  xen mapcache: check if memory region has moved.
  xen: record physmap changes to xenstore
  Set runstate to INMIGRATE earlier
  Introduce "xen-save-devices-state"
  cirrus_vga: do not reset videoram

Conflicts:
	qapi-schema.json

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2012-03-19 13:39:42 -05:00
commit 33cf629a37
9 changed files with 278 additions and 12 deletions

View file

@ -0,0 +1,34 @@
= Save Devices =
QEMU has code to load/save the state of the guest that it is running.
These are two complementary operations. Saving the state just does
that, saves the state for each device that the guest is running.
These operations are normally used with migration (see migration.txt),
however it is also possible to save the state of all devices to file,
without saving the RAM or the block devices of the VM.
This operation is called "xen-save-devices-state" (see
QMP/qmp-commands.txt)
The binary format used in the file is the following:
-------------------------------------------
32 bit big endian: QEMU_VM_FILE_MAGIC
32 bit big endian: QEMU_VM_FILE_VERSION
for_each_device
{
8 bit: QEMU_VM_SECTION_FULL
32 bit big endian: section_id
8 bit: idstr (ID string) length
string: idstr (ID string)
32 bit big endian: instance_id
32 bit big endian: version_id
buffer: device specific data
}
8 bit: QEMU_VM_EOF

View file

@ -2767,10 +2767,6 @@ static void cirrus_reset(void *opaque)
}
s->vga.cr[0x27] = s->device_id;
/* Win2K seems to assume that the pattern buffer is at 0xff
initially ! */
memset(s->vga.vram_ptr, 0xff, s->real_vram_size);
s->cirrus_hidden_dac_lockindex = 5;
s->cirrus_hidden_dac_data = 0;
}

View file

@ -1684,3 +1684,20 @@
##
{ 'command': 'migrate',
'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
# @xen-save-devices-state:
#
# Save the state of all devices to file. The RAM and the block devices
# of the VM are not saved by this command.
#
# @filename: the file to save the state of the devices to as binary
# data. See xen-save-devices-state.txt for a description of the binary
# format.
#
# Returns: Nothing on success
# If @filename cannot be opened, OpenFileFailed
# If an I/O error occurs while writing the file, IOError
#
# Since: 1.1
##
{ 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }

View file

@ -441,6 +441,33 @@ Example:
Note: inject-nmi is only supported for x86 guest currently, it will
returns "Unsupported" error for non-x86 guest.
EQMP
{
.name = "xen-save-devices-state",
.args_type = "filename:F",
.mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state,
},
SQMP
xen-save-devices-state
-------
Save the state of all devices to file. The RAM and the block devices
of the VM are not saved by this command.
Arguments:
- "filename": the file to save the state of the devices to as binary
data. See xen-save-devices-state.txt for a description of the binary
format.
Example:
-> { "execute": "xen-save-devices-state",
"arguments": { "filename": "/tmp/save" } }
<- { "return": {} }
EQMP
{

View file

@ -84,6 +84,7 @@
#include "qemu-timer.h"
#include "cpus.h"
#include "memory.h"
#include "qmp-commands.h"
#define SELF_ANNOUNCE_ROUNDS 5
@ -1177,6 +1178,7 @@ typedef struct SaveStateEntry {
void *opaque;
CompatEntry *compat;
int no_migrate;
int is_ram;
} SaveStateEntry;
@ -1241,6 +1243,10 @@ int register_savevm_live(DeviceState *dev,
se->opaque = opaque;
se->vmsd = NULL;
se->no_migrate = 0;
/* if this is a live_savem then set is_ram */
if (save_live_state != NULL) {
se->is_ram = 1;
}
if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
char *id = dev->parent_bus->info->get_dev_path(dev);
@ -1728,6 +1734,45 @@ out:
return ret;
}
static int qemu_save_device_state(QEMUFile *f)
{
SaveStateEntry *se;
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
cpu_synchronize_all_states();
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
int len;
if (se->is_ram) {
continue;
}
if (se->save_state == NULL && se->vmsd == NULL) {
continue;
}
/* Section type */
qemu_put_byte(f, QEMU_VM_SECTION_FULL);
qemu_put_be32(f, se->section_id);
/* ID string */
len = strlen(se->idstr);
qemu_put_byte(f, len);
qemu_put_buffer(f, (uint8_t *)se->idstr, len);
qemu_put_be32(f, se->instance_id);
qemu_put_be32(f, se->version_id);
vmstate_save(f, se);
}
qemu_put_byte(f, QEMU_VM_EOF);
return qemu_file_get_error(f);
}
static SaveStateEntry *find_se(const char *idstr, int instance_id)
{
SaveStateEntry *se;
@ -2109,6 +2154,32 @@ void do_savevm(Monitor *mon, const QDict *qdict)
vm_start();
}
void qmp_xen_save_devices_state(const char *filename, Error **errp)
{
QEMUFile *f;
int saved_vm_running;
int ret;
saved_vm_running = runstate_is_running();
vm_stop(RUN_STATE_SAVE_VM);
f = qemu_fopen(filename, "wb");
if (!f) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
goto the_end;
}
ret = qemu_save_device_state(f);
qemu_fclose(f);
if (ret < 0) {
error_set(errp, QERR_IO_ERROR);
}
the_end:
if (saved_vm_running)
vm_start();
return;
}
int load_vmstate(const char *name)
{
BlockDriverState *bs, *bs_vm_state;

2
vl.c
View file

@ -3099,6 +3099,7 @@ int main(int argc, char **argv, char **envp)
break;
case QEMU_OPTION_incoming:
incoming = optarg;
runstate_set(RUN_STATE_INMIGRATE);
break;
case QEMU_OPTION_nodefaults:
default_serial = 0;
@ -3616,7 +3617,6 @@ int main(int argc, char **argv, char **envp)
}
if (incoming) {
runstate_set(RUN_STATE_INMIGRATE);
int ret = qemu_start_incoming_migration(incoming);
if (ret < 0) {
fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",

104
xen-all.c
View file

@ -65,7 +65,7 @@ static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu)
typedef struct XenPhysmap {
target_phys_addr_t start_addr;
ram_addr_t size;
MemoryRegion *mr;
char *name;
target_phys_addr_t phys_offset;
QLIST_ENTRY(XenPhysmap) list;
@ -190,6 +190,14 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr)
xen_pfn_t *pfn_list;
int i;
if (runstate_check(RUN_STATE_INMIGRATE)) {
/* RAM already populated in Xen */
fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT
" bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n",
__func__, size, ram_addr);
return;
}
if (mr == &ram_memory) {
return;
}
@ -225,6 +233,22 @@ static XenPhysmap *get_physmapping(XenIOState *state,
return NULL;
}
static target_phys_addr_t xen_phys_offset_to_gaddr(target_phys_addr_t start_addr,
ram_addr_t size, void *opaque)
{
target_phys_addr_t addr = start_addr & TARGET_PAGE_MASK;
XenIOState *xen_io_state = opaque;
XenPhysmap *physmap = NULL;
QLIST_FOREACH(physmap, &xen_io_state->physmap, list) {
if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) {
return physmap->start_addr;
}
}
return start_addr;
}
#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 340
static int xen_add_to_physmap(XenIOState *state,
target_phys_addr_t start_addr,
@ -237,6 +261,7 @@ static int xen_add_to_physmap(XenIOState *state,
XenPhysmap *physmap = NULL;
target_phys_addr_t pfn, start_gpfn;
target_phys_addr_t phys_offset = memory_region_get_ram_addr(mr);
char path[80], value[17];
if (get_physmapping(state, start_addr, size)) {
return 0;
@ -275,6 +300,7 @@ go_physmap:
physmap->start_addr = start_addr;
physmap->size = size;
physmap->name = (char *)mr->name;
physmap->phys_offset = phys_offset;
QLIST_INSERT_HEAD(&state->physmap, physmap, list);
@ -283,6 +309,30 @@ go_physmap:
start_addr >> TARGET_PAGE_BITS,
(start_addr + size) >> TARGET_PAGE_BITS,
XEN_DOMCTL_MEM_CACHEATTR_WB);
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr",
xen_domid, (uint64_t)phys_offset);
snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)start_addr);
if (!xs_write(state->xenstore, 0, path, value, strlen(value))) {
return -1;
}
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%"PRIx64"/size",
xen_domid, (uint64_t)phys_offset);
snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)size);
if (!xs_write(state->xenstore, 0, path, value, strlen(value))) {
return -1;
}
if (mr->name) {
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%"PRIx64"/name",
xen_domid, (uint64_t)phys_offset);
if (!xs_write(state->xenstore, 0, path, mr->name, strlen(mr->name))) {
return -1;
}
}
return 0;
}
@ -942,6 +992,55 @@ int xen_init(void)
return 0;
}
static void xen_read_physmap(XenIOState *state)
{
XenPhysmap *physmap = NULL;
unsigned int len, num, i;
char path[80], *value = NULL;
char **entries = NULL;
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap", xen_domid);
entries = xs_directory(state->xenstore, 0, path, &num);
if (entries == NULL)
return;
for (i = 0; i < num; i++) {
physmap = g_malloc(sizeof (XenPhysmap));
physmap->phys_offset = strtoull(entries[i], NULL, 16);
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%s/start_addr",
xen_domid, entries[i]);
value = xs_read(state->xenstore, 0, path, &len);
if (value == NULL) {
free(physmap);
continue;
}
physmap->start_addr = strtoull(value, NULL, 16);
free(value);
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%s/size",
xen_domid, entries[i]);
value = xs_read(state->xenstore, 0, path, &len);
if (value == NULL) {
free(physmap);
continue;
}
physmap->size = strtoull(value, NULL, 16);
free(value);
snprintf(path, sizeof(path),
"/local/domain/0/device-model/%d/physmap/%s/name",
xen_domid, entries[i]);
physmap->name = xs_read(state->xenstore, 0, path, &len);
QLIST_INSERT_HEAD(&state->physmap, physmap, list);
}
free(entries);
return;
}
int xen_hvm_init(void)
{
int i, rc;
@ -999,7 +1098,7 @@ int xen_hvm_init(void)
}
/* Init RAM management */
xen_map_cache_init();
xen_map_cache_init(xen_phys_offset_to_gaddr, state);
xen_ram_init(ram_size);
qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state);
@ -1017,6 +1116,7 @@ int xen_hvm_init(void)
xen_be_register("console", &xen_console_ops);
xen_be_register("vkbd", &xen_kbdmouse_ops);
xen_be_register("qdisk", &xen_blkdev_ops);
xen_read_physmap(state);
return 0;
}

View file

@ -78,6 +78,9 @@ typedef struct MapCache {
uint8_t *last_address_vaddr;
unsigned long max_mcache_size;
unsigned int mcache_bucket_shift;
phys_offset_to_gaddr_t phys_offset_to_gaddr;
void *opaque;
} MapCache;
static MapCache *mapcache;
@ -91,13 +94,16 @@ static inline int test_bits(int nr, int size, const unsigned long *addr)
return 0;
}
void xen_map_cache_init(void)
void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
{
unsigned long size;
struct rlimit rlimit_as;
mapcache = g_malloc0(sizeof (MapCache));
mapcache->phys_offset_to_gaddr = f;
mapcache->opaque = opaque;
QTAILQ_INIT(&mapcache->locked_entries);
mapcache->last_address_index = -1;
@ -193,9 +199,14 @@ uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
uint8_t lock)
{
MapCacheEntry *entry, *pentry = NULL;
target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
target_phys_addr_t address_index;
target_phys_addr_t address_offset;
target_phys_addr_t __size = size;
bool translated = false;
tryagain:
address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
trace_xen_map_cache(phys_addr);
@ -237,6 +248,11 @@ uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
mapcache->last_address_index = -1;
if (!translated && mapcache->phys_offset_to_gaddr) {
phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque);
translated = true;
goto tryagain;
}
trace_xen_map_cache_return(NULL);
return NULL;
}

View file

@ -11,9 +11,13 @@
#include <stdlib.h>
typedef target_phys_addr_t (*phys_offset_to_gaddr_t)(target_phys_addr_t start_addr,
ram_addr_t size,
void *opaque);
#ifdef CONFIG_XEN
void xen_map_cache_init(void);
void xen_map_cache_init(phys_offset_to_gaddr_t f,
void *opaque);
uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
uint8_t lock);
ram_addr_t xen_ram_addr_from_mapcache(void *ptr);
@ -22,7 +26,8 @@ void xen_invalidate_map_cache(void);
#else
static inline void xen_map_cache_init(void)
static inline void xen_map_cache_init(phys_offset_to_gaddr_t f,
void *opaque)
{
}