aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-02-25 15:30:57 +0000
committerPeter Maydell <peter.maydell@linaro.org>2016-02-25 15:30:57 +0000
commit586fc27e6a2ade22e35089e8972bc678b113e1db (patch)
tree3757f6e443da156b390e913c0c8437c3d5e4ddb4
parent774ae4254d3910f1c94ad6ed44d14cbea0e6a2f2 (diff)
parentcfc3b074de4b4ccee2540edbf8cfdb026dc19943 (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* Asynchronous dump-guest-memory from Peter * improved logging with -D -daemonize from Dimitris * more address_space_* optimization from Gonglei * TCG xsave/xrstor thinko fix * chardev bugfix and documentation patch # gpg: Signature made Thu 25 Feb 2016 15:12:27 GMT using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" * remotes/bonzini/tags/for-upstream: target-i386: fix confusion in xcr0 bit position vs. mask chardev: Properly initialize ChardevCommon components memory: Remove unreachable return statement memory: optimize qemu_get_ram_ptr and qemu_ram_ptr_length exec: store RAMBlock pointer into memory region log: Redirect stderr to logfile if deamonized dump-guest-memory: add qmp event DUMP_COMPLETED Dump: add hmp command "info dump" Dump: add qmp command "query-dump" DumpState: adding total_size and written_size fields dump-guest-memory: add "detach" support dump-guest-memory: disable dump when in INMIGRATE state dump-guest-memory: introduce dump_process() helper function. dump-guest-memory: add dump_in_progress() helper function dump-guest-memory: using static DumpState, add DumpStatus dump-guest-memory: add "detach" flag for QMP/HMP interfaces. dump-guest-memory: cleanup: removing dump_{error|cleanup}(). scripts/kvm/kvm_stat: Fix missing right parantheses and ".format(...)" qemu-options.hx: Improve documentation of chardev multiplexing mode Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--docs/qmp-events.txt18
-rw-r--r--dump.c215
-rw-r--r--exec.c48
-rw-r--r--hmp-commands-info.hx14
-rw-r--r--hmp-commands.hx5
-rw-r--r--hmp.c26
-rw-r--r--hmp.h1
-rw-r--r--include/exec/memory.h8
-rw-r--r--include/qemu-common.h4
-rw-r--r--include/qemu/log.h6
-rw-r--r--include/sysemu/char.h10
-rw-r--r--include/sysemu/dump.h15
-rw-r--r--include/sysemu/memory_mapping.h4
-rw-r--r--memory.c3
-rw-r--r--memory_mapping.c3
-rw-r--r--os-posix.c6
-rw-r--r--qapi-schema.json56
-rw-r--r--qapi/event.json16
-rw-r--r--qemu-char.c2
-rw-r--r--qemu-doc.texi30
-rw-r--r--qemu-options.hx45
-rw-r--r--qmp-commands.hx31
-rw-r--r--qmp.c14
-rwxr-xr-xscripts/kvm/kvm_stat5
-rw-r--r--spice-qemu-char.c12
-rw-r--r--target-i386/cpu.c29
-rw-r--r--target-i386/cpu.h29
-rw-r--r--target-i386/fpu_helper.c41
-rw-r--r--target-i386/mpx_helper.c2
-rw-r--r--ui/console.c20
-rw-r--r--util/log.c11
31 files changed, 564 insertions, 165 deletions
diff --git a/docs/qmp-events.txt b/docs/qmp-events.txt
index 52eb7e2f5e..4e3eb9e77a 100644
--- a/docs/qmp-events.txt
+++ b/docs/qmp-events.txt
@@ -220,6 +220,24 @@ Data:
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+DUMP_COMPLETED
+--------------
+
+Emitted when the guest has finished one memory dump.
+
+Data:
+
+- "result": DumpQueryResult type described in qapi-schema.json
+- "error": Error message when dump failed. This is only a
+ human-readable string provided when dump failed. It should not be
+ parsed in any way (json-string, optional)
+
+Example:
+
+{ "event": "DUMP_COMPLETED",
+ "data": {"result": {"total": 1090650112, "status": "completed",
+ "completed": 1090650112} } }
+
GUEST_PANICKED
--------------
diff --git a/dump.c b/dump.c
index 96e1fc15de..81d2d4f5a0 100644
--- a/dump.c
+++ b/dump.c
@@ -25,6 +25,7 @@
#include "sysemu/cpus.h"
#include "qapi/qmp/qerror.h"
#include "qmp-commands.h"
+#include "qapi-event.h"
#include <zlib.h>
#ifdef CONFIG_LZO
@@ -82,12 +83,6 @@ static int dump_cleanup(DumpState *s)
return 0;
}
-static void dump_error(DumpState *s, const char *reason, Error **errp)
-{
- dump_cleanup(s);
- error_setg(errp, "%s", reason);
-}
-
static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
{
DumpState *s = opaque;
@@ -128,7 +123,7 @@ static void write_elf64_header(DumpState *s, Error **errp)
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write elf header", errp);
+ error_setg(errp, "dump: failed to write elf header");
}
}
@@ -159,7 +154,7 @@ static void write_elf32_header(DumpState *s, Error **errp)
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write elf header", errp);
+ error_setg(errp, "dump: failed to write elf header");
}
}
@@ -182,7 +177,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write program header table", errp);
+ error_setg(errp, "dump: failed to write program header table");
}
}
@@ -205,7 +200,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write program header table", errp);
+ error_setg(errp, "dump: failed to write program header table");
}
}
@@ -225,7 +220,7 @@ static void write_elf64_note(DumpState *s, Error **errp)
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write program header table", errp);
+ error_setg(errp, "dump: failed to write program header table");
}
}
@@ -245,7 +240,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
id = cpu_index(cpu);
ret = cpu_write_elf64_note(f, cpu, id, s);
if (ret < 0) {
- dump_error(s, "dump: failed to write elf notes", errp);
+ error_setg(errp, "dump: failed to write elf notes");
return;
}
}
@@ -253,7 +248,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
CPU_FOREACH(cpu) {
ret = cpu_write_elf64_qemunote(f, cpu, s);
if (ret < 0) {
- dump_error(s, "dump: failed to write CPU status", errp);
+ error_setg(errp, "dump: failed to write CPU status");
return;
}
}
@@ -275,7 +270,7 @@ static void write_elf32_note(DumpState *s, Error **errp)
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) {
- dump_error(s, "dump: failed to write program header table", errp);
+ error_setg(errp, "dump: failed to write program header table");
}
}
@@ -290,7 +285,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
id = cpu_index(cpu);
ret = cpu_write_elf32_note(f, cpu, id, s);
if (ret < 0) {
- dump_error(s, "dump: failed to write elf notes", errp);
+ error_setg(errp, "dump: failed to write elf notes");
return;
}
}
@@ -298,7 +293,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
CPU_FOREACH(cpu) {
ret = cpu_write_elf32_qemunote(f, cpu, s);
if (ret < 0) {
- dump_error(s, "dump: failed to write CPU status", errp);
+ error_setg(errp, "dump: failed to write CPU status");
return;
}
}
@@ -326,7 +321,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp)
ret = fd_write_vmcore(&shdr, shdr_size, s);
if (ret < 0) {
- dump_error(s, "dump: failed to write section header table", errp);
+ error_setg(errp, "dump: failed to write section header table");
}
}
@@ -336,7 +331,9 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp)
ret = fd_write_vmcore(buf, length, s);
if (ret < 0) {
- dump_error(s, "dump: failed to save memory", errp);
+ error_setg(errp, "dump: failed to save memory");
+ } else {
+ s->written_size += length;
}
}
@@ -568,11 +565,6 @@ static void dump_begin(DumpState *s, Error **errp)
}
}
-static void dump_completed(DumpState *s)
-{
- dump_cleanup(s);
-}
-
static int get_next_block(DumpState *s, GuestPhysBlock *block)
{
while (1) {
@@ -624,8 +616,6 @@ static void dump_iterate(DumpState *s, Error **errp)
}
} while (!get_next_block(s, block));
-
- dump_completed(s);
}
static void create_vmcore(DumpState *s, Error **errp)
@@ -765,7 +755,7 @@ static void create_header32(DumpState *s, Error **errp)
dh->status = cpu_to_dump32(s, status);
if (write_buffer(s->fd, 0, dh, size) < 0) {
- dump_error(s, "dump: failed to write disk dump header", errp);
+ error_setg(errp, "dump: failed to write disk dump header");
goto out;
}
@@ -784,7 +774,7 @@ static void create_header32(DumpState *s, Error **errp)
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) {
- dump_error(s, "dump: failed to write kdump sub header", errp);
+ error_setg(errp, "dump: failed to write kdump sub header");
goto out;
}
@@ -800,7 +790,7 @@ static void create_header32(DumpState *s, Error **errp)
}
if (write_buffer(s->fd, offset_note, s->note_buf,
s->note_size) < 0) {
- dump_error(s, "dump: failed to write notes", errp);
+ error_setg(errp, "dump: failed to write notes");
goto out;
}
@@ -865,7 +855,7 @@ static void create_header64(DumpState *s, Error **errp)
dh->status = cpu_to_dump32(s, status);
if (write_buffer(s->fd, 0, dh, size) < 0) {
- dump_error(s, "dump: failed to write disk dump header", errp);
+ error_setg(errp, "dump: failed to write disk dump header");
goto out;
}
@@ -884,7 +874,7 @@ static void create_header64(DumpState *s, Error **errp)
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) {
- dump_error(s, "dump: failed to write kdump sub header", errp);
+ error_setg(errp, "dump: failed to write kdump sub header");
goto out;
}
@@ -901,7 +891,7 @@ static void create_header64(DumpState *s, Error **errp)
if (write_buffer(s->fd, offset_note, s->note_buf,
s->note_size) < 0) {
- dump_error(s, "dump: failed to write notes", errp);
+ error_setg(errp, "dump: failed to write notes");
goto out;
}
@@ -1087,7 +1077,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
while (get_next_page(&block_iter, &pfn, NULL, s)) {
ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
if (ret < 0) {
- dump_error(s, "dump: failed to set dump_bitmap", errp);
+ error_setg(errp, "dump: failed to set dump_bitmap");
goto out;
}
@@ -1104,7 +1094,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false,
dump_bitmap_buf, s);
if (ret < 0) {
- dump_error(s, "dump: failed to sync dump_bitmap", errp);
+ error_setg(errp, "dump: failed to sync dump_bitmap");
goto out;
}
}
@@ -1237,7 +1227,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf, s->dump_info.page_size, false);
g_free(buf);
if (ret < 0) {
- dump_error(s, "dump: failed to write page data (zero page)", errp);
+ error_setg(errp, "dump: failed to write page data (zero page)");
goto out;
}
@@ -1253,7 +1243,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page desc", errp);
+ error_setg(errp, "dump: failed to write page desc");
goto out;
}
} else {
@@ -1278,7 +1268,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page data", errp);
+ error_setg(errp, "dump: failed to write page data");
goto out;
}
#ifdef CONFIG_LZO
@@ -1291,7 +1281,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page data", errp);
+ error_setg(errp, "dump: failed to write page data");
goto out;
}
#endif
@@ -1305,7 +1295,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page data", errp);
+ error_setg(errp, "dump: failed to write page data");
goto out;
}
#endif
@@ -1321,7 +1311,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf,
s->dump_info.page_size, false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page data", errp);
+ error_setg(errp, "dump: failed to write page data");
goto out;
}
}
@@ -1333,20 +1323,21 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false);
if (ret < 0) {
- dump_error(s, "dump: failed to write page desc", errp);
+ error_setg(errp, "dump: failed to write page desc");
goto out;
}
}
+ s->written_size += s->dump_info.page_size;
}
ret = write_cache(&page_desc, NULL, 0, true);
if (ret < 0) {
- dump_error(s, "dump: failed to sync cache for page_desc", errp);
+ error_setg(errp, "dump: failed to sync cache for page_desc");
goto out;
}
ret = write_cache(&page_data, NULL, 0, true);
if (ret < 0) {
- dump_error(s, "dump: failed to sync cache for page_data", errp);
+ error_setg(errp, "dump: failed to sync cache for page_data");
goto out;
}
@@ -1390,7 +1381,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
ret = write_start_flat_header(s->fd);
if (ret < 0) {
- dump_error(s, "dump: failed to write start flat header", errp);
+ error_setg(errp, "dump: failed to write start flat header");
return;
}
@@ -1414,11 +1405,9 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
ret = write_end_flat_header(s->fd);
if (ret < 0) {
- dump_error(s, "dump: failed to write end flat header", errp);
+ error_setg(errp, "dump: failed to write end flat header");
return;
}
-
- dump_completed(s);
}
static ram_addr_t get_start_block(DumpState *s)
@@ -1457,6 +1446,44 @@ static void get_max_mapnr(DumpState *s)
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
}
+static DumpState dump_state_global = { .status = DUMP_STATUS_NONE };
+
+static void dump_state_prepare(DumpState *s)
+{
+ /* zero the struct, setting status to active */
+ *s = (DumpState) { .status = DUMP_STATUS_ACTIVE };
+}
+
+bool dump_in_progress(void)
+{
+ DumpState *state = &dump_state_global;
+ return (atomic_read(&state->status) == DUMP_STATUS_ACTIVE);
+}
+
+/* calculate total size of memory to be dumped (taking filter into
+ * acoount.) */
+static int64_t dump_calculate_size(DumpState *s)
+{
+ GuestPhysBlock *block;
+ int64_t size = 0, total = 0, left = 0, right = 0;
+
+ QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
+ if (s->has_filter) {
+ /* calculate the overlapped region. */
+ left = MAX(s->begin, block->target_start);
+ right = MIN(s->begin + s->length, block->target_end);
+ size = right - left;
+ size = size > 0 ? size : 0;
+ } else {
+ /* count the whole region in */
+ size = (block->target_end - block->target_start);
+ }
+ total += size;
+ }
+
+ return total;
+}
+
static void dump_init(DumpState *s, int fd, bool has_format,
DumpGuestMemoryFormat format, bool paging, bool has_filter,
int64_t begin, int64_t length, Error **errp)
@@ -1466,6 +1493,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
Error *err = NULL;
int ret;
+ s->has_format = has_format;
+ s->format = format;
+ s->written_size = 0;
+
/* kdump-compressed is conflict with paging and filter */
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
assert(!paging && !has_filter);
@@ -1496,6 +1527,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
guest_phys_blocks_init(&s->guest_phys_blocks);
guest_phys_blocks_append(&s->guest_phys_blocks);
+ s->total_size = dump_calculate_size(s);
+#ifdef DEBUG_DUMP_GUEST_MEMORY
+ fprintf(stderr, "DUMP: total memory to dump: %lu\n", s->total_size);
+#endif
s->start = get_start_block(s);
if (s->start == -1) {
@@ -1624,8 +1659,60 @@ cleanup:
dump_cleanup(s);
}
-void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
- int64_t begin, bool has_length,
+/* this operation might be time consuming. */
+static void dump_process(DumpState *s, Error **errp)
+{
+ Error *local_err = NULL;
+ DumpQueryResult *result = NULL;
+
+ if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
+ create_kdump_vmcore(s, &local_err);
+ } else {
+ create_vmcore(s, &local_err);
+ }
+
+ /* make sure status is written after written_size updates */
+ smp_wmb();
+ atomic_set(&s->status,
+ (local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED));
+
+ /* send DUMP_COMPLETED message (unconditionally) */
+ result = qmp_query_dump(NULL);
+ /* should never fail */
+ assert(result);
+ qapi_event_send_dump_completed(result, !!local_err, (local_err ? \
+ error_get_pretty(local_err) : NULL),
+ &error_abort);
+ qapi_free_DumpQueryResult(result);
+
+ error_propagate(errp, local_err);
+ dump_cleanup(s);
+}
+
+static void *dump_thread(void *data)
+{
+ Error *err = NULL;
+ DumpState *s = (DumpState *)data;
+ dump_process(s, &err);
+ error_free(err);
+ return NULL;
+}
+
+DumpQueryResult *qmp_query_dump(Error **errp)
+{
+ DumpQueryResult *result = g_new(DumpQueryResult, 1);
+ DumpState *state = &dump_state_global;
+ result->status = atomic_read(&state->status);
+ /* make sure we are reading status and written_size in order */
+ smp_rmb();
+ result->completed = state->written_size;
+ result->total = state->total_size;
+ return result;
+}
+
+void qmp_dump_guest_memory(bool paging, const char *file,
+ bool has_detach, bool detach,
+ bool has_begin, int64_t begin, bool has_length,
int64_t length, bool has_format,
DumpGuestMemoryFormat format, Error **errp)
{
@@ -1633,6 +1720,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
int fd = -1;
DumpState *s;
Error *local_err = NULL;
+ bool detach_p = false;
+
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ error_setg(errp, "Dump not allowed during incoming migration.");
+ return;
+ }
+
+ /* if there is a dump in background, we should wait until the dump
+ * finished */
+ if (dump_in_progress()) {
+ error_setg(errp, "There is a dump in process, please wait.");
+ return;
+ }
/*
* kdump-compressed format need the whole memory dumped, so paging or
@@ -1652,6 +1752,9 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
error_setg(errp, QERR_MISSING_PARAMETER, "begin");
return;
}
+ if (has_detach) {
+ detach_p = detach;
+ }
/* check whether lzo/snappy is supported */
#ifndef CONFIG_LZO
@@ -1690,23 +1793,25 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
return;
}
- s = g_malloc0(sizeof(DumpState));
+ s = &dump_state_global;
+ dump_state_prepare(s);
dump_init(s, fd, has_format, format, paging, has_begin,
begin, length, &local_err);
if (local_err) {
- g_free(s);
error_propagate(errp, local_err);
+ atomic_set(&s->status, DUMP_STATUS_FAILED);
return;
}
- if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
- create_kdump_vmcore(s, errp);
+ if (detach_p) {
+ /* detached dump */
+ qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
+ s, QEMU_THREAD_DETACHED);
} else {
- create_vmcore(s, errp);
+ /* sync dump */
+ dump_process(s, errp);
}
-
- g_free(s);
}
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)
diff --git a/exec.c b/exec.c
index 1f2450002b..c62c43903c 100644
--- a/exec.c
+++ b/exec.c
@@ -1717,6 +1717,8 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
error_propagate(errp, local_err);
return -1;
}
+
+ mr->ram_block = new_block;
return addr;
}
@@ -1866,9 +1868,13 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
*
* Called within RCU critical section.
*/
-void *qemu_get_ram_ptr(ram_addr_t addr)
+void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
{
- RAMBlock *block = qemu_get_ram_block(addr);
+ RAMBlock *block = ram_block;
+
+ if (block == NULL) {
+ block = qemu_get_ram_block(addr);
+ }
if (xen_enabled() && block->host == NULL) {
/* We need to check if the requested address is in the RAM
@@ -1889,15 +1895,18 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
*
* Called within RCU critical section.
*/
-static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size)
+static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
+ hwaddr *size)
{
- RAMBlock *block;
+ RAMBlock *block = ram_block;
ram_addr_t offset_inside_block;
if (*size == 0) {
return NULL;
}
- block = qemu_get_ram_block(addr);
+ if (block == NULL) {
+ block = qemu_get_ram_block(addr);
+ }
offset_inside_block = addr - block->offset;
*size = MIN(*size, block->max_length - offset_inside_block);
@@ -2025,13 +2034,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
}
switch (size) {
case 1:
- stb_p(qemu_get_ram_ptr(ram_addr), val);
+ stb_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break;
case 2:
- stw_p(qemu_get_ram_ptr(ram_addr), val);
+ stw_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break;
case 4:
- stl_p(qemu_get_ram_ptr(ram_addr), val);
+ stl_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break;
default:
abort();
@@ -2607,7 +2616,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
} else {
addr1 += memory_region_get_ram_addr(mr);
/* RAM case */
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
memcpy(ptr, buf, l);
invalidate_and_set_dirty(mr, addr1, l);
}
@@ -2698,7 +2707,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
}
} else {
/* RAM case */
- ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr + addr1);
memcpy(buf, ptr, l);
}
@@ -2783,7 +2792,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
} else {
addr1 += memory_region_get_ram_addr(mr);
/* ROM/RAM case */
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (type) {
case WRITE_DATA:
memcpy(ptr, buf, l);
@@ -2995,7 +3004,7 @@ void *address_space_map(AddressSpace *as,
memory_region_ref(mr);
*plen = done;
- ptr = qemu_ram_ptr_length(raddr + base, plen);
+ ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen);
rcu_read_unlock();
return ptr;
@@ -3079,7 +3088,8 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
#endif
} else {
/* RAM case */
- ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
+ ptr = qemu_get_ram_ptr(mr->ram_block,
+ (memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK)
+ addr1);
switch (endian) {
@@ -3174,7 +3184,8 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
#endif
} else {
/* RAM case */
- ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
+ ptr = qemu_get_ram_ptr(mr->ram_block,
+ (memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK)
+ addr1);
switch (endian) {
@@ -3289,7 +3300,8 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
#endif
} else {
/* RAM case */
- ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
+ ptr = qemu_get_ram_ptr(mr->ram_block,
+ (memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK)
+ addr1);
switch (endian) {
@@ -3374,7 +3386,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
} else {
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
stl_p(ptr, val);
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
@@ -3429,7 +3441,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
} else {
/* RAM case */
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (endian) {
case DEVICE_LITTLE_ENDIAN:
stl_le_p(ptr, val);
@@ -3539,7 +3551,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
} else {
/* RAM case */
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (endian) {
case DEVICE_LITTLE_ENDIAN:
stw_le_p(ptr, val);
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 9b71351d01..52539c3109 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -786,6 +786,20 @@ STEXI
Display the value of a storage key (s390 only)
ETEXI
+ {
+ .name = "dump",
+ .args_type = "",
+ .params = "",
+ .help = "Display the latest dump status",
+ .mhandler.cmd = hmp_info_dump,
+ },
+
+STEXI
+@item info dump
+@findex dump
+Display the latest dump status.
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/hmp-commands.hx b/hmp-commands.hx
index bb52e4d3bd..664d794f29 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1056,10 +1056,11 @@ ETEXI
{
.name = "dump-guest-memory",
- .args_type = "paging:-p,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
- .params = "[-p] [-z|-l|-s] filename [begin length]",
+ .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
+ .params = "[-p] [-d] [-z|-l|-s] filename [begin length]",
.help = "dump guest memory into file 'filename'.\n\t\t\t"
"-p: do paging to get guest's memory mapping.\n\t\t\t"
+ "-d: return immediately (do not wait for completion).\n\t\t\t"
"-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t"
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"
diff --git a/hmp.c b/hmp.c
index d00c2d4a7c..5b6084a56a 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1599,8 +1599,10 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
const char *file = qdict_get_str(qdict, "filename");
bool has_begin = qdict_haskey(qdict, "begin");
bool has_length = qdict_haskey(qdict, "length");
+ bool has_detach = qdict_haskey(qdict, "detach");
int64_t begin = 0;
int64_t length = 0;
+ bool detach = false;
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
char *prot;
@@ -1628,11 +1630,14 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
if (has_length) {
length = qdict_get_int(qdict, "length");
}
+ if (has_detach) {
+ detach = qdict_get_bool(qdict, "detach");
+ }
prot = g_strconcat("file:", file, NULL);
- qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
- true, dump_format, &err);
+ qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin,
+ has_length, length, true, dump_format, &err);
hmp_handle_error(mon, &err);
g_free(prot);
}
@@ -2358,3 +2363,20 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
qapi_free_RockerOfDpaGroupList(list);
}
+
+void hmp_info_dump(Monitor *mon, const QDict *qdict)
+{
+ DumpQueryResult *result = qmp_query_dump(NULL);
+
+ assert(result && result->status < DUMP_STATUS__MAX);
+ monitor_printf(mon, "Status: %s\n", DumpStatus_lookup[result->status]);
+
+ if (result->status == DUMP_STATUS_ACTIVE) {
+ float percent = 0;
+ assert(result->total != 0);
+ percent = 100.0 * result->completed / result->total;
+ monitor_printf(mon, "Finished: %.2f %%\n", percent);
+ }
+
+ qapi_free_DumpQueryResult(result);
+}
diff --git a/hmp.h b/hmp.h
index a8c5b5a9a6..093d65f5a3 100644
--- a/hmp.h
+++ b/hmp.h
@@ -131,5 +131,6 @@ void hmp_rocker(Monitor *mon, const QDict *qdict);
void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
+void hmp_info_dump(Monitor *mon, const QDict *qdict);
#endif
diff --git a/include/exec/memory.h b/include/exec/memory.h
index d5f2b2ce6f..d5284c2435 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -31,6 +31,7 @@
#include "qemu/notify.h"
#include "qom/object.h"
#include "qemu/rcu.h"
+#include "qemu/typedefs.h"
#define MAX_PHYS_ADDR_SPACE_BITS 62
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
@@ -169,6 +170,7 @@ struct MemoryRegion {
bool global_locking;
uint8_t dirty_log_mask;
ram_addr_t ram_addr;
+ RAMBlock *ram_block;
Object *owner;
const MemoryRegionIOMMUOps *iommu_ops;
@@ -1386,7 +1388,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
MemoryRegion *mr);
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len);
-void *qemu_get_ram_ptr(ram_addr_t addr);
+void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
{
@@ -1395,8 +1397,6 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
} else {
return memory_region_is_ram(mr) || memory_region_is_romd(mr);
}
-
- return false;
}
/**
@@ -1427,7 +1427,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
mr = address_space_translate(as, addr, &addr1, &l, false);
if (len == l && memory_access_is_direct(mr, false)) {
addr1 += memory_region_get_ram_addr(mr);
- ptr = qemu_get_ram_ptr(addr1);
+ ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
memcpy(buf, ptr, len);
} else {
result = address_space_read_continue(as, addr, attrs, buf, len,
diff --git a/include/qemu-common.h b/include/qemu-common.h
index a85e9bf98a..ced2994402 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -493,4 +493,8 @@ int parse_debug_env(const char *name, int max, int initial);
const char *qemu_ether_ntoa(const MACAddr *mac);
void page_size_init(void);
+/* returns non-zero if dump is in progress, otherwise zero is
+ * returned. */
+bool dump_in_progress(void);
+
#endif
diff --git a/include/qemu/log.h b/include/qemu/log.h
index 1afac63760..40c24fda40 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -90,12 +90,6 @@ static inline void qemu_log_close(void)
}
}
-/* Set up a new log file */
-static inline void qemu_log_set_file(FILE *f)
-{
- qemu_logfile = f;
-}
-
/* define log items */
typedef struct QEMULogItem {
int mask;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index e035d1cbda..e46884f367 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -115,6 +115,16 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
Error **errp);
/**
+ * @qemu_chr_parse_common:
+ *
+ * Parse the common options available to all character backends.
+ *
+ * @opts the options that still need parsing
+ * @backend a new backend
+ */
+void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
+
+/**
* @qemu_chr_new:
*
* Create a new character backend from a URI.
diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h
index 2f04b247be..ef931be469 100644
--- a/include/sysemu/dump.h
+++ b/include/sysemu/dump.h
@@ -38,6 +38,7 @@
#include "sysemu/dump-arch.h"
#include "sysemu/memory_mapping.h"
+#include "qapi-types.h"
typedef struct QEMU_PACKED MakedumpfileHeader {
char signature[16]; /* = "makedumpfile" */
@@ -176,6 +177,20 @@ typedef struct DumpState {
off_t offset_page; /* offset of page part in vmcore */
size_t num_dumpable; /* number of page that can be dumped */
uint32_t flag_compress; /* indicate the compression format */
+ DumpStatus status; /* current dump status */
+
+ bool has_format; /* whether format is provided */
+ DumpGuestMemoryFormat format; /* valid only if has_format == true */
+ QemuThread dump_thread; /* thread for detached dump */
+
+ int64_t total_size; /* total memory size (in bytes) to
+ * be dumped. When filter is
+ * enabled, this will only count
+ * those to be written. */
+ int64_t written_size; /* written memory size (in bytes),
+ * this could be used to calculate
+ * how much work we have
+ * finished. */
} DumpState;
uint16_t cpu_to_dump16(DumpState *s, uint16_t val);
diff --git a/include/sysemu/memory_mapping.h b/include/sysemu/memory_mapping.h
index a75d59a55d..d46d879b94 100644
--- a/include/sysemu/memory_mapping.h
+++ b/include/sysemu/memory_mapping.h
@@ -16,6 +16,7 @@
#include "qemu/queue.h"
#include "qemu/typedefs.h"
+#include "exec/memory.h"
typedef struct GuestPhysBlock {
/* visible to guest, reflects PCI hole, etc */
@@ -27,6 +28,9 @@ typedef struct GuestPhysBlock {
/* points into host memory */
uint8_t *host_addr;
+ /* points to the MemoryRegion that this block belongs to */
+ MemoryRegion *mr;
+
QTAILQ_ENTRY(GuestPhysBlock) next;
} GuestPhysBlock;
diff --git a/memory.c b/memory.c
index 09041edd43..0dd9695aec 100644
--- a/memory.c
+++ b/memory.c
@@ -912,6 +912,7 @@ void memory_region_init(MemoryRegion *mr,
}
mr->name = g_strdup(name);
mr->owner = owner;
+ mr->ram_block = NULL;
if (name) {
char *escaped_name = memory_region_escape_name(name);
@@ -1569,7 +1570,7 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
mr = mr->alias;
}
assert(mr->ram_addr != RAM_ADDR_INVALID);
- ptr = qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK);
+ ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr & TARGET_PAGE_MASK);
rcu_read_unlock();
return ptr + offset;
diff --git a/memory_mapping.c b/memory_mapping.c
index 04db3ac7fa..c8855de92b 100644
--- a/memory_mapping.c
+++ b/memory_mapping.c
@@ -178,6 +178,7 @@ void guest_phys_blocks_free(GuestPhysBlockList *list)
QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
QTAILQ_REMOVE(&list->head, p, next);
+ memory_region_unref(p->mr);
g_free(p);
}
list->num = 0;
@@ -241,6 +242,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener,
block->target_start = target_start;
block->target_end = target_end;
block->host_addr = host_addr;
+ block->mr = section->mr;
+ memory_region_ref(section->mr);
QTAILQ_INSERT_TAIL(&g->list->head, block, next);
++g->list->num;
diff --git a/os-posix.c b/os-posix.c
index cce62ed92b..92fa3baa1a 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -37,6 +37,7 @@
#include "qemu-options.h"
#include "qemu/rcu.h"
#include "qemu/error-report.h"
+#include "qemu/log.h"
#ifdef CONFIG_LINUX
#include <sys/prctl.h>
@@ -275,7 +276,10 @@ void os_setup_post(void)
dup2(fd, 0);
dup2(fd, 1);
- dup2(fd, 2);
+ /* In case -D is given do not redirect stderr to /dev/null */
+ if (!qemu_logfile) {
+ dup2(fd, 2);
+ }
close(fd);
diff --git a/qapi-schema.json b/qapi-schema.json
index 8d04897922..7b8f2a13e6 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2195,6 +2195,10 @@
# 2. fd: the protocol starts with "fd:", and the following string
# is the fd's name.
#
+# @detach: #optional if true, QMP will return immediately rather than
+# waiting for the dump to finish. The user can track progress
+# using "query-dump". (since 2.6).
+#
# @begin: #optional if specified, the starting physical address.
#
# @length: #optional if specified, the memory size, in bytes. If you don't
@@ -2211,8 +2215,56 @@
# Since: 1.2
##
{ 'command': 'dump-guest-memory',
- 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
- '*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
+ 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool',
+ '*begin': 'int', '*length': 'int',
+ '*format': 'DumpGuestMemoryFormat'} }
+
+##
+# @DumpStatus
+#
+# Describe the status of a long-running background guest memory dump.
+#
+# @none: no dump-guest-memory has started yet.
+#
+# @active: there is one dump running in background.
+#
+# @completed: the last dump has finished successfully.
+#
+# @failed: the last dump has failed.
+#
+# Since 2.6
+##
+{ 'enum': 'DumpStatus',
+ 'data': [ 'none', 'active', 'completed', 'failed' ] }
+
+##
+# @DumpQueryResult
+#
+# The result format for 'query-dump'.
+#
+# @status: enum of @DumpStatus, which shows current dump status
+#
+# @completed: bytes written in latest dump (uncompressed)
+#
+# @total: total bytes to be written in latest dump (uncompressed)
+#
+# Since 2.6
+##
+{ 'struct': 'DumpQueryResult',
+ 'data': { 'status': 'DumpStatus',
+ 'completed': 'int',
+ 'total': 'int' } }
+
+##
+# @query-dump
+#
+# Query latest dump status.
+#
+# Returns: A @DumpStatus object showing the dump status.
+#
+# Since: 2.6
+##
+{ 'command': 'query-dump', 'returns': 'DumpQueryResult' }
##
# @DumpGuestMemoryCapability:
diff --git a/qapi/event.json b/qapi/event.json
index 390fd45788..1a45a6cb26 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -369,3 +369,19 @@
##
{ 'event': 'MEM_UNPLUG_ERROR',
'data': { 'device': 'str', 'msg': 'str' } }
+
+##
+# @DUMP_COMPLETED
+#
+# Emitted when background dump has completed
+#
+# @result: DumpQueryResult type described in qapi-schema.json.
+#
+# @error: #optional human-readable error string that provides
+# hint on why dump failed. Only presents on failure. The
+# user should not try to interpret the error string.
+#
+# Since: 2.6
+##
+{ 'event': 'DUMP_COMPLETED' ,
+ 'data': { 'result': 'DumpQueryResult', '*error': 'str' } }
diff --git a/qemu-char.c b/qemu-char.c
index ad11b75e3d..fc8ffda157 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3490,7 +3490,7 @@ fail:
return NULL;
}
-static void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
+void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
{
const char *logfile = qemu_opt_get(opts, "logfile");
diff --git a/qemu-doc.texi b/qemu-doc.texi
index c324da8b61..bc9dd13cc9 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -158,7 +158,8 @@ TODO (no longer available)
* pcsys_introduction:: Introduction
* pcsys_quickstart:: Quick Start
* sec_invocation:: Invocation
-* pcsys_keys:: Keys
+* pcsys_keys:: Keys in the graphical frontends
+* mux_keys:: Keys in the character backend multiplexer
* pcsys_monitor:: QEMU Monitor
* disk_images:: Disk Images
* pcsys_network:: Network emulation
@@ -272,7 +273,7 @@ targets do not need a disk image.
@c man end
@node pcsys_keys
-@section Keys
+@section Keys in the graphical frontends
@c man begin OPTIONS
@@ -322,15 +323,23 @@ Toggle mouse and keyboard grab.
In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down},
@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log.
-@kindex Ctrl-a h
-During emulation, if you are using the @option{-nographic} option, use
-@key{Ctrl-a h} to get terminal commands:
+@c man end
+
+@node mux_keys
+@section Keys in the character backend multiplexer
+
+@c man begin OPTIONS
+
+During emulation, if you are using a character backend multiplexer
+(which is the default if you are using @option{-nographic}) then
+several commands are available via an escape sequence. These
+key sequences all start with an escape character, which is @key{Ctrl-a}
+by default, but can be changed with @option{-echr}. The list below assumes
+you're using the default.
@table @key
@item Ctrl-a h
@kindex Ctrl-a h
-@item Ctrl-a ?
-@kindex Ctrl-a ?
Print this help
@item Ctrl-a x
@kindex Ctrl-a x
@@ -346,10 +355,11 @@ Toggle console timestamps
Send break (magic sysrq in Linux)
@item Ctrl-a c
@kindex Ctrl-a c
-Switch between console and monitor
+Rotate between the frontends connected to the multiplexer (usually
+this switches between the monitor and the console)
@item Ctrl-a Ctrl-a
-@kindex Ctrl-a a
-Send Ctrl-a
+@kindex Ctrl-a Ctrl-a
+Send the escape character to the frontend
@end table
@c man end
diff --git a/qemu-options.hx b/qemu-options.hx
index f528405810..599db9474c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2166,8 +2166,49 @@ All devices must have an id, which can be any string up to 127 characters long.
It is used to uniquely identify this device in other command line directives.
A character device may be used in multiplexing mode by multiple front-ends.
-The key sequence of @key{Control-a} and @key{c} will rotate the input focus
-between attached front-ends. Specify @option{mux=on} to enable this mode.
+Specify @option{mux=on} to enable this mode.
+A multiplexer is a "1:N" device, and here the "1" end is your specified chardev
+backend, and the "N" end is the various parts of QEMU that can talk to a chardev.
+If you create a chardev with @option{id=myid} and @option{mux=on}, QEMU will
+create a multiplexer with your specified ID, and you can then configure multiple
+front ends to use that chardev ID for their input/output. Up to four different
+front ends can be connected to a single multiplexed chardev. (Without
+multiplexing enabled, a chardev can only be used by a single front end.)
+For instance you could use this to allow a single stdio chardev to be used by
+two serial ports and the QEMU monitor:
+
+@example
+-chardev stdio,mux=on,id=char0 \
+-mon chardev=char0,mode=readline,default \
+-serial chardev:char0 \
+-serial chardev:char0
+@end example
+
+You can have more than one multiplexer in a system configuration; for instance
+you could have a TCP port multiplexed between UART 0 and UART 1, and stdio
+multiplexed between the QEMU monitor and a parallel port:
+
+@example
+-chardev stdio,mux=on,id=char0 \
+-mon chardev=char0,mode=readline,default \
+-parallel chardev:char0 \
+-chardev tcp,...,mux=on,id=char1 \
+-serial chardev:char1 \
+-serial chardev:char1
+@end example
+
+When you're using a multiplexed character device, some escape sequences are
+interpreted in the input. @xref{mux_keys, Keys in the character backend
+multiplexer}.
+
+Note that some other command line options may implicitly create multiplexed
+character backends; for instance @option{-serial mon:stdio} creates a
+multiplexed stdio backend connected to the serial port and the QEMU monitor,
+and @option{-nographic} also multiplexes the console and the monitor to
+stdio.
+
+There is currently no support for multiplexing in the other direction
+(where a single QEMU front end takes input and output from multiple chardevs).
Every backend supports the @option{logfile} option, which supplies the path
to a file to record all data transmitted via the backend. The @option{logappend}
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 085dc7d5cd..13f158d568 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -838,8 +838,8 @@ EQMP
{
.name = "dump-guest-memory",
- .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
- .params = "-p protocol [begin] [length] [format]",
+ .args_type = "paging:b,protocol:s,detach:b?,begin:i?,end:i?,format:s?",
+ .params = "-p protocol [-d] [begin] [length] [format]",
.help = "dump guest memory to file",
.mhandler.cmd_new = qmp_marshal_dump_guest_memory,
},
@@ -855,6 +855,9 @@ Arguments:
- "paging": do paging to get guest's memory mapping (json-bool)
- "protocol": destination file(started with "file:") or destination file
descriptor (started with "fd:") (json-string)
+- "detach": if specified, command will return immediately, without waiting
+ for the dump to finish. The user can track progress using
+ "query-dump". (json-bool)
- "begin": the starting physical address. It's optional, and should be specified
with length together (json-int)
- "length": the memory size, in bytes. It's optional, and should be specified
@@ -894,6 +897,30 @@ Example:
EQMP
+ {
+ .name = "query-dump",
+ .args_type = "",
+ .params = "",
+ .help = "query background dump status",
+ .mhandler.cmd_new = qmp_marshal_query_dump,
+ },
+
+SQMP
+query-dump
+----------
+
+Query background dump status.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "query-dump" }
+<- { "return": { "status": "active", "completed": 1024000,
+ "total": 2048000 } }
+
+EQMP
+
#if defined TARGET_S390X
{
.name = "dump-skeys",
diff --git a/qmp.c b/qmp.c
index 9a93d5e246..3f16a77b44 100644
--- a/qmp.c
+++ b/qmp.c
@@ -103,6 +103,13 @@ void qmp_quit(Error **errp)
void qmp_stop(Error **errp)
{
+ /* if there is a dump in background, we should wait until the dump
+ * finished */
+ if (dump_in_progress()) {
+ error_setg(errp, "There is a dump in process, please wait.");
+ return;
+ }
+
if (runstate_check(RUN_STATE_INMIGRATE)) {
autostart = 0;
} else {
@@ -175,6 +182,13 @@ void qmp_cont(Error **errp)
BlockBackend *blk;
BlockDriverState *bs;
+ /* if there is a dump in background, we should wait until the dump
+ * finished */
+ if (dump_in_progress()) {
+ error_setg(errp, "There is a dump in process, please wait.");
+ return;
+ }
+
if (runstate_needs_reset()) {
error_setg(errp, "Resetting the Virtual Machine is required");
return;
diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat
index 3cf1181750..769d884b6d 100755
--- a/scripts/kvm/kvm_stat
+++ b/scripts/kvm/kvm_stat
@@ -796,11 +796,12 @@ def check_access(options):
sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
"when using the option -t (default).\n"
"If it is enabled, make {0} readable by the "
- "current user.\n")
+ "current user.\n"
+ .format(PATH_DEBUGFS_TRACING))
if options.tracepoints:
sys.exit(1)
- sys.stderr.write("Falling back to debugfs statistics!\n"
+ sys.stderr.write("Falling back to debugfs statistics!\n")
options.debugfs = True
sleep(5)
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 7c1f4385bf..21885c526b 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -366,26 +366,30 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *name = qemu_opt_get(opts, "name");
+ ChardevSpiceChannel *spicevmc;
if (name == NULL) {
error_setg(errp, "chardev: spice channel: no name given");
return;
}
- backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
- backend->u.spicevmc->type = g_strdup(name);
+ spicevmc = backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
+ spicevmc->type = g_strdup(name);
}
static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *name = qemu_opt_get(opts, "name");
+ ChardevSpicePort *spiceport;
if (name == NULL) {
error_setg(errp, "chardev: spice port: no name given");
return;
}
- backend->u.spiceport = g_new0(ChardevSpicePort, 1);
- backend->u.spiceport->fqdn = g_strdup(name);
+ spiceport = backend->u.spiceport = g_new0(ChardevSpicePort, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
+ spiceport->fqdn = g_strdup(name);
}
static void register_types(void)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index c78f824678..0f38d1eae3 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -470,19 +470,26 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
#undef REGISTER
const ExtSaveArea x86_ext_save_areas[] = {
- [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
+ [XSTATE_YMM_BIT] =
+ { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
.offset = 0x240, .size = 0x100 },
- [3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
+ [XSTATE_BNDREGS_BIT] =
+ { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = 0x3c0, .size = 0x40 },
- [4] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
+ [XSTATE_BNDCSR_BIT] =
+ { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = 0x400, .size = 0x40 },
- [5] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
+ [XSTATE_OPMASK_BIT] =
+ { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x440, .size = 0x40 },
- [6] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
+ [XSTATE_ZMM_Hi256_BIT] =
+ { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x480, .size = 0x200 },
- [7] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
+ [XSTATE_Hi16_ZMM_BIT] =
+ { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x680, .size = 0x400 },
- [9] = { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU,
+ [XSTATE_PKRU_BIT] =
+ { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU,
.offset = 0xA80, .size = 0x8 },
};
@@ -2480,7 +2487,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ecx = MAX(*ecx, esa->offset + esa->size);
}
}
- *eax |= ena_mask & (XSTATE_FP | XSTATE_SSE);
+ *eax |= ena_mask & (XSTATE_FP_MASK | XSTATE_SSE_MASK);
*ebx = *ecx;
} else if (count == 1) {
*eax = env->features[FEAT_XSAVE];
@@ -2714,15 +2721,15 @@ static void x86_cpu_reset(CPUState *s)
cpu_watchpoint_remove_all(s, BP_CPU);
cr4 = 0;
- xcr0 = XSTATE_FP;
+ xcr0 = XSTATE_FP_MASK;
#ifdef CONFIG_USER_ONLY
/* Enable all the features for user-mode. */
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
- xcr0 |= XSTATE_SSE;
+ xcr0 |= XSTATE_SSE_MASK;
}
if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_MPX) {
- xcr0 |= XSTATE_BNDREGS | XSTATE_BNDCSR;
+ xcr0 |= XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK;
}
if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK;
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 7febc3f5ec..5148c8252d 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -404,16 +404,25 @@
#define MSR_IA32_BNDCFGS 0x00000d90
#define MSR_IA32_XSS 0x00000da0
-#define XSTATE_FP (1ULL << 0)
-#define XSTATE_SSE (1ULL << 1)
-#define XSTATE_YMM (1ULL << 2)
-#define XSTATE_BNDREGS (1ULL << 3)
-#define XSTATE_BNDCSR (1ULL << 4)
-#define XSTATE_OPMASK (1ULL << 5)
-#define XSTATE_ZMM_Hi256 (1ULL << 6)
-#define XSTATE_Hi16_ZMM (1ULL << 7)
-#define XSTATE_PKRU (1ULL << 9)
-
+#define XSTATE_FP_BIT 0
+#define XSTATE_SSE_BIT 1
+#define XSTATE_YMM_BIT 2
+#define XSTATE_BNDREGS_BIT 3
+#define XSTATE_BNDCSR_BIT 4
+#define XSTATE_OPMASK_BIT 5
+#define XSTATE_ZMM_Hi256_BIT 6
+#define XSTATE_Hi16_ZMM_BIT 7
+#define XSTATE_PKRU_BIT 9
+
+#define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT)
+#define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT)
+#define XSTATE_YMM_MASK (1ULL << XSTATE_YMM_BIT)
+#define XSTATE_BNDREGS_MASK (1ULL << XSTATE_BNDREGS_BIT)
+#define XSTATE_BNDCSR_MASK (1ULL << XSTATE_BNDCSR_BIT)
+#define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT)
+#define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT)
+#define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT)
+#define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT)
/* CPUID feature words */
typedef enum FeatureWord {
diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c
index 9dfbc4c7a6..d1a7f4cbec 100644
--- a/target-i386/fpu_helper.c
+++ b/target-i386/fpu_helper.c
@@ -1215,7 +1215,7 @@ static uint64_t get_xinuse(CPUX86State *env)
indicate in use. That said, the state of BNDREGS is important
enough to track in HFLAGS, so we might as well use that here. */
if ((env->hflags & HF_MPX_IU_MASK) == 0) {
- inuse &= ~XSTATE_BNDREGS;
+ inuse &= ~XSTATE_BNDREGS_MASK;
}
return inuse;
}
@@ -1239,22 +1239,22 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm,
rfbm &= env->xcr0;
opt &= rfbm;
- if (opt & XSTATE_FP) {
+ if (opt & XSTATE_FP_MASK) {
do_xsave_fpu(env, ptr, ra);
}
- if (rfbm & XSTATE_SSE) {
+ if (rfbm & XSTATE_SSE_MASK) {
/* Note that saving MXCSR is not suppressed by XSAVEOPT. */
do_xsave_mxcsr(env, ptr, ra);
}
- if (opt & XSTATE_SSE) {
+ if (opt & XSTATE_SSE_MASK) {
do_xsave_sse(env, ptr, ra);
}
- if (opt & XSTATE_BNDREGS) {
- target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
+ if (opt & XSTATE_BNDREGS_MASK) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
do_xsave_bndregs(env, ptr + off, ra);
}
- if (opt & XSTATE_BNDCSR) {
- target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
+ if (opt & XSTATE_BNDCSR_MASK) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
do_xsave_bndcsr(env, ptr + off, ra);
}
@@ -1399,19 +1399,19 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
raise_exception_ra(env, EXCP0D_GPF, ra);
}
- if (rfbm & XSTATE_FP) {
- if (xstate_bv & XSTATE_FP) {
+ if (rfbm & XSTATE_FP_MASK) {
+ if (xstate_bv & XSTATE_FP_MASK) {
do_xrstor_fpu(env, ptr, ra);
} else {
helper_fninit(env);
memset(env->fpregs, 0, sizeof(env->fpregs));
}
}
- if (rfbm & XSTATE_SSE) {
+ if (rfbm & XSTATE_SSE_MASK) {
/* Note that the standard form of XRSTOR loads MXCSR from memory
whether or not the XSTATE_BV bit is set. */
do_xrstor_mxcsr(env, ptr, ra);
- if (xstate_bv & XSTATE_SSE) {
+ if (xstate_bv & XSTATE_SSE_MASK) {
do_xrstor_sse(env, ptr, ra);
} else {
/* ??? When AVX is implemented, we may have to be more
@@ -1419,9 +1419,9 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
memset(env->xmm_regs, 0, sizeof(env->xmm_regs));
}
}
- if (rfbm & XSTATE_BNDREGS) {
- if (xstate_bv & XSTATE_BNDREGS) {
- target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
+ if (rfbm & XSTATE_BNDREGS_MASK) {
+ if (xstate_bv & XSTATE_BNDREGS_MASK) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
do_xrstor_bndregs(env, ptr + off, ra);
env->hflags |= HF_MPX_IU_MASK;
} else {
@@ -1429,9 +1429,9 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
env->hflags &= ~HF_MPX_IU_MASK;
}
}
- if (rfbm & XSTATE_BNDCSR) {
- if (xstate_bv & XSTATE_BNDCSR) {
- target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
+ if (rfbm & XSTATE_BNDCSR_MASK) {
+ if (xstate_bv & XSTATE_BNDCSR_MASK) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
do_xrstor_bndcsr(env, ptr + off, ra);
} else {
memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs));
@@ -1470,7 +1470,7 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
}
/* Only XCR0 is defined at present; the FPU may not be disabled. */
- if (ecx != 0 || (mask & XSTATE_FP) == 0) {
+ if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) {
goto do_gpf;
}
@@ -1482,7 +1482,8 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
}
/* Disallow enabling only half of MPX. */
- if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) {
+ if ((mask ^ (mask * (XSTATE_BNDCSR_MASK / XSTATE_BNDREGS_MASK)))
+ & XSTATE_BNDCSR_MASK) {
goto do_gpf;
}
diff --git a/target-i386/mpx_helper.c b/target-i386/mpx_helper.c
index 052a69c52f..4d1785ecef 100644
--- a/target-i386/mpx_helper.c
+++ b/target-i386/mpx_helper.c
@@ -36,7 +36,7 @@ void cpu_sync_bndcs_hflags(CPUX86State *env)
}
if ((env->cr[4] & CR4_OSXSAVE_MASK)
- && (env->xcr0 & XSTATE_BNDCSR)
+ && (env->xcr0 & XSTATE_BNDCSR_MASK)
&& (bndcsr & BNDCFG_ENABLE)) {
hflags |= HF_MPX_EN_MASK;
} else {
diff --git a/ui/console.c b/ui/console.c
index b739ae9a05..7db0fd27c9 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2060,31 +2060,33 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
int val;
+ ChardevVC *vc;
- backend->u.vc = g_new0(ChardevVC, 1);
+ vc = backend->u.vc = g_new0(ChardevVC, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
val = qemu_opt_get_number(opts, "width", 0);
if (val != 0) {
- backend->u.vc->has_width = true;
- backend->u.vc->width = val;
+ vc->has_width = true;
+ vc->width = val;
}
val = qemu_opt_get_number(opts, "height", 0);
if (val != 0) {
- backend->u.vc->has_height = true;
- backend->u.vc->height = val;
+ vc->has_height = true;
+ vc->height = val;
}
val = qemu_opt_get_number(opts, "cols", 0);
if (val != 0) {
- backend->u.vc->has_cols = true;
- backend->u.vc->cols = val;
+ vc->has_cols = true;
+ vc->cols = val;
}
val = qemu_opt_get_number(opts, "rows", 0);
if (val != 0) {
- backend->u.vc->has_rows = true;
- backend->u.vc->rows = val;
+ vc->has_rows = true;
+ vc->rows = val;
}
}
diff --git a/util/log.c b/util/log.c
index 2709e98f98..a7ddc7ecd1 100644
--- a/util/log.c
+++ b/util/log.c
@@ -56,13 +56,20 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
#ifdef CONFIG_TRACE_LOG
qemu_loglevel |= LOG_TRACE;
#endif
- if (qemu_loglevel && !qemu_logfile) {
+ if ((qemu_loglevel || is_daemonized()) && !qemu_logfile) {
if (logfilename) {
qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
if (!qemu_logfile) {
perror(logfilename);
_exit(1);
}
+ /* In case we are a daemon redirect stderr to logfile */
+ if (is_daemonized()) {
+ dup2(fileno(qemu_logfile), STDERR_FILENO);
+ fclose(qemu_logfile);
+ /* This will skip closing logfile in qemu_log_close() */
+ qemu_logfile = stderr;
+ }
} else {
/* Default to stderr if no log file specified */
qemu_logfile = stderr;
@@ -82,7 +89,7 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
log_append = 1;
}
}
- if (!qemu_loglevel && qemu_logfile) {
+ if (!qemu_loglevel && !is_daemonized() && qemu_logfile) {
qemu_log_close();
}
}