The new monitor command dump may take long time to finish. So we need run it at the background.
Signed-off-by: Wen Congyang <we...@cn.fujitsu.com> --- dump.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- vl.c | 5 +- 2 files changed, 150 insertions(+), 23 deletions(-) diff --git a/dump.c b/dump.c index 48779d8..8224116 100644 --- a/dump.c +++ b/dump.c @@ -78,9 +78,21 @@ typedef struct DumpState { bool resume; char *error; target_phys_addr_t memory_offset; + + /* + * Return value: + * -2: EAGAIN + * -1: error + * 0: success + */ write_core_dump_function f; void (*cleanup)(void *opaque); + int (*dump_begin_iterate)(struct DumpState *, void *opaque); void *opaque; + RAMBlock *block; + ram_addr_t start; + target_phys_addr_t offset; + VMChangeStateEntry *handler; } DumpState; static DumpState *dump_get_current(void) @@ -98,6 +110,12 @@ static int dump_cleanup(DumpState *s) memory_mapping_list_free(&s->list); s->cleanup(s->opaque); + + if (s->handler) { + qemu_del_vm_change_state_handler(s->handler); + s->handler = NULL; + } + if (s->resume) { vm_start(); } @@ -323,40 +341,70 @@ static int write_elf32_notes(DumpState *s, int phdr_index, return 0; } +/* + * Return value: + * -2: blocked + * -1: failed + * 0: sucess + */ static int write_data(DumpState *s, void *buf, int length, target_phys_addr_t *offset) { int ret; ret = s->f(*offset, buf, length, s->opaque); - if (ret < 0) { + if (ret == -1) { dump_error(s, "dump: failed to save memory.\n"); return -1; } + if (ret == -2) { + return -2; + } + *offset += length; return 0; } /* write the memroy to vmcore. 1 page per I/O. */ -static int write_memory(DumpState *s, RAMBlock *block, - target_phys_addr_t *offset) +static int write_memory(DumpState *s, RAMBlock *block, ram_addr_t start, + target_phys_addr_t *offset, int64_t *size, + int64_t deadline) { int i, ret; + int64_t writen_size = 0; + int64_t time; - for (i = 0; i < block->length / TARGET_PAGE_SIZE; i++) { - ret = write_data(s, block->host + i * TARGET_PAGE_SIZE, + *size = block->length - start; + for (i = 0; i < *size / TARGET_PAGE_SIZE; i++) { + ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE, TARGET_PAGE_SIZE, offset); if (ret < 0) { - return -1; + *size = writen_size; + return ret; + } + + writen_size += TARGET_PAGE_SIZE; + time = qemu_get_clock_ms(rt_clock); + if (time >= deadline) { + /* time out */ + *size = writen_size; + return -2; } } - if ((block->length % TARGET_PAGE_SIZE) != 0) { - ret = write_data(s, block->host + i * TARGET_PAGE_SIZE, - block->length % TARGET_PAGE_SIZE, offset); + if ((*size % TARGET_PAGE_SIZE) != 0) { + ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE, + *size % TARGET_PAGE_SIZE, offset); if (ret < 0) { - return -1; + *size = writen_size; + return ret; + } + + time = qemu_get_clock_ms(rt_clock); + if (time >= deadline) { + /* time out */ + return -2; } } @@ -435,6 +483,7 @@ static int dump_begin(DumpState *s) } s->memory_offset = offset; + s->offset = offset; return 0; } @@ -462,22 +511,65 @@ static int dump_completed(DumpState *s) return 0; } -/* write all memory to vmcore */ -static int dump_iterate(DumpState *s) +static int get_next_block(DumpState *s, RAMBlock *block) { + while (1) { + block = QLIST_NEXT(block, next); + if (!block) { + /* no more block */ + return 1; + } + + s->start = 0; + s->block = block; + + return 0; + } +} + +/* write memory to vmcore */ +static void dump_iterate(void *opaque) +{ + DumpState *s = opaque; RAMBlock *block; - target_phys_addr_t offset = s->memory_offset; + target_phys_addr_t offset = s->offset; + int64_t size; + int64_t deadline, now; int ret; - /* write all memory to vmcore */ - QLIST_FOREACH(block, &ram_list.blocks, next) { - ret = write_memory(s, block, &offset); - if (ret < 0) { - return -1; + now = qemu_get_clock_ms(rt_clock); + deadline = now + 5; + while(1) { + block = s->block; + ret = write_memory(s, block, s->start, &offset, &size, deadline); + if (ret == -1) { + return; + } + + if (ret == -2) { + break; + } + + ret = get_next_block(s, block); + if (ret == 1) { + dump_completed(s); + return; } } - return dump_completed(s); + if (size == block->length - s->start) { + ret = get_next_block(s, block); + if (ret == 1) { + dump_completed(s); + return; + } + } else { + s->start += size; + } + + s->offset = offset; + + return; } static int create_vmcore(DumpState *s) @@ -489,7 +581,7 @@ static int create_vmcore(DumpState *s) return -1; } - ret = dump_iterate(s); + ret = s->dump_begin_iterate(s, s->opaque); if (ret < 0) { return -1; } @@ -497,6 +589,17 @@ static int create_vmcore(DumpState *s) return 0; } +static void dump_vm_state_change(void *opaque, int running, RunState state) +{ + DumpState *s = opaque; + + if (running) { + qmp_dump_cancel(NULL); + s->state = DUMP_STATE_ERROR; + s->error = g_strdup("vm state is changed to run\n"); + } +} + static DumpState *dump_init(Error **errp) { CPUState *env; @@ -514,6 +617,9 @@ static DumpState *dump_init(Error **errp) g_free(s->error); s->error = NULL; } + s->block = QLIST_FIRST(&ram_list.blocks); + s->start = 0; + s->handler = qemu_add_vm_change_state_handler(dump_vm_state_change, s); /* * get dump info: endian, class and architecture. @@ -560,14 +666,24 @@ static int fd_write_vmcore(target_phys_addr_t offset, void *buf, size_t size, ret = lseek(fd, offset, SEEK_SET); if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return -2; + } return -1; } ret = write(fd, buf, size); - if (ret != size) { + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return -2; + } return -1; } + if (ret != size) { + return -2; + } + return 0; } @@ -576,10 +692,18 @@ static void fd_cleanup(void *opaque) int fd = (int)(intptr_t)opaque; if (fd != -1) { + qemu_set_fd_handler(fd, NULL, NULL, NULL); close(fd); } } +static int fd_dump_begin_iterate(DumpState *s, void *opaque) +{ + int fd = (int)(intptr_t)opaque; + + return qemu_set_fd_handler(fd, NULL, dump_iterate, s); +} + static DumpState *dump_init_fd(int fd, Error **errp) { DumpState *s = dump_init(errp); @@ -590,7 +714,9 @@ static DumpState *dump_init_fd(int fd, Error **errp) s->f = fd_write_vmcore; s->cleanup = fd_cleanup; + s->dump_begin_iterate = fd_dump_begin_iterate; s->opaque = (void *)(intptr_t)fd; + fcntl(fd, F_SETFL, O_NONBLOCK); return s; } diff --git a/vl.c b/vl.c index 1d4c350..fcc1c57 100644 --- a/vl.c +++ b/vl.c @@ -1248,11 +1248,12 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) void vm_state_notify(int running, RunState state) { - VMChangeStateEntry *e; + VMChangeStateEntry *e, *next; trace_vm_state_notify(running, state); - for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) { + /* e->cb() may remove itself */ + QLIST_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { e->cb(e->opaque, running, state); } } -- 1.7.1