Patchwork [RFC,12/14,v7] run dump at the background

login
register
mail settings
Submitter Wen Congyang
Date March 1, 2012, 2:54 a.m.
Message ID <4F4EE4D8.1080207@cn.fujitsu.com>
Download mbox | patch
Permalink /patch/143887/
State New
Headers show

Comments

Wen Congyang - March 1, 2012, 2:54 a.m.
The new monitor command dump may take long time to finish. So we need run it
at the background.

Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
 dump.c |  168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 vl.c   |    5 +-
 2 files changed, 150 insertions(+), 23 deletions(-)

Patch

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);
     }
 }