[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v6 2/6] BitmapLog: bitmap dump code
From: |
Sanidhya Kashyap |
Subject: |
[Qemu-devel] [PATCH v6 2/6] BitmapLog: bitmap dump code |
Date: |
Sat, 13 Sep 2014 10:00:10 -0400 |
Signed-off-by: Sanidhya Kashyap <address@hidden>
---
No functional change, except:
- naming convention i.e. QemuProcess has been changed to QemuDirtyBitmapUser.
- rectified mistakes in documentation in qapi-schema.json.
- removed acronyms
hmp-commands.hx | 16 ++
hmp.c | 18 +++
hmp.h | 1 +
include/exec/cpu-all.h | 5 +-
include/sysemu/sysemu.h | 5 +
migration.c | 12 ++
qapi-schema.json | 37 +++++
qmp-commands.hx | 32 ++++
savevm.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++++
vl.c | 23 +++
10 files changed, 526 insertions(+), 1 deletion(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index f859f8d..d104232 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1786,6 +1786,22 @@ STEXI
show available trace events and their state
ETEXI
+ {
+ .name = "log_dirty_bitmap",
+ .args_type = "filename:s,iterations:i?,period:i?",
+ .params = "filename iterations period",
+ .help = "dumps the memory's dirty bitmap to file\n\t\t\t"
+ "filename: name of the file in which the bitmap will be
saved\n\t\t\t"
+ "iterations: number of times the memory will be
logged\n\t\t\t"
+ "period: time difference in milliseconds between each
iteration",
+ .mhandler.cmd = hmp_log_dirty_bitmap,
+ },
+STEXI
address@hidden log_dirty_bitmap @var{filename}
address@hidden log_dirty_bitmap
+dumps the writable working set of a VM's memory to a file
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/hmp.c b/hmp.c
index 4d1838e..d067420 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1318,6 +1318,24 @@ void hmp_device_del(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
+void hmp_log_dirty_bitmap(Monitor *mon, const QDict *qdict)
+{
+ const char *filename = qdict_get_str(qdict, "filename");
+ bool has_iterations = qdict_haskey(qdict, "iterations");
+ int64_t iterations = qdict_get_try_int(qdict, "iterations", 3);
+ bool has_period = qdict_haskey(qdict, "period");
+ int64_t period = qdict_get_try_int(qdict, "period", 10);
+ Error *err = NULL;
+
+ qmp_log_dirty_bitmap(filename, has_iterations, iterations,
+ has_period, period, &err);
+ if (err) {
+ monitor_printf(mon, "log_dirty_bitmap: %s\n", error_get_pretty(err));
+ error_free(err);
+ return;
+ }
+}
+
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
diff --git a/hmp.h b/hmp.h
index 4fd3c4a..0895182 100644
--- a/hmp.h
+++ b/hmp.h
@@ -94,6 +94,7 @@ void hmp_cpu_add(Monitor *mon, const QDict *qdict);
void hmp_object_add(Monitor *mon, const QDict *qdict);
void hmp_object_del(Monitor *mon, const QDict *qdict);
void hmp_info_memdev(Monitor *mon, const QDict *qdict);
+void hmp_log_dirty_bitmap(Monitor *mon, const QDict *qdict);
void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index f9d132f..4824d36 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -299,13 +299,16 @@ CPUArchState *cpu_copy(CPUArchState *env);
/* memory API */
+/* global name which is used with both migration and bitmap dump */
+#define RAMBLOCK_NAME_LENGTH 256
+
typedef struct RAMBlock {
struct MemoryRegion *mr;
uint8_t *host;
ram_addr_t offset;
ram_addr_t length;
uint32_t flags;
- char idstr[256];
+ char idstr[RAMBLOCK_NAME_LENGTH];
/* Reads can take either the iothread or the ramlist lock.
* Writes must take both locks.
*/
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index d8539fd..b5525ea 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -227,4 +227,9 @@ extern QemuOptsList qemu_net_opts;
extern QemuOptsList qemu_global_opts;
extern QemuOptsList qemu_mon_opts;
+/* migration vs dirty bitmap process */
+bool qemu_process_check(QemuDirtyBitmapUser user);
+void qemu_process_set(QemuDirtyBitmapUser user);
+const char *get_qemu_dirty_bitmap_user_as_string(void);
+
#endif
diff --git a/migration.c b/migration.c
index 8d675b3..6a02b40 100644
--- a/migration.c
+++ b/migration.c
@@ -117,6 +117,7 @@ static void process_incoming_migration_co(void *opaque)
} else {
runstate_set(RUN_STATE_PAUSED);
}
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_NONE);
}
void process_incoming_migration(QEMUFile *f)
@@ -317,6 +318,7 @@ static void migrate_fd_cleanup(void *opaque)
}
notifier_list_notify(&migration_state_notifiers, s);
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_NONE);
}
void migrate_fd_error(MigrationState *s)
@@ -326,6 +328,7 @@ void migrate_fd_error(MigrationState *s)
s->state = MIG_STATE_ERROR;
trace_migrate_set_state(MIG_STATE_ERROR);
notifier_list_notify(&migration_state_notifiers, s);
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_NONE);
}
static void migrate_fd_cancel(MigrationState *s)
@@ -436,6 +439,15 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
return;
}
+ if (!qemu_process_check(QEMU_DIRTY_BITMAP_USER_NONE) &&
+ !qemu_process_check(QEMU_DIRTY_BITMAP_USER_MIGRATION)) {
+ error_setg(errp, "Migration not possible, since %s "
+ "is in progress.", get_qemu_dirty_bitmap_user_as_string());
+ return;
+ }
+
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_MIGRATION);
+
s = migrate_init(¶ms);
if (strstart(uri, "tcp:", &p)) {
diff --git a/qapi-schema.json b/qapi-schema.json
index 689b548..f96e959 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3481,3 +3481,40 @@
# Since: 2.1
##
{ 'command': 'rtc-reset-reinjection' }
+
+##
+# QemuDirtyBitmapUser
+#
+# @none: no other process is being executed besides a simple VM execution.
+#
+# @migration: migration process is going on.
+#
+# @bitmap-dump: bitmap dump process is being executed.
+#
+# Since 2.2
+##
+{ 'enum': 'QemuDirtyBitmapUser',
+ 'data': [ 'none', 'migration', 'bitmap-dump' ] }
+
+##
+# @log-dirty-bitmap
+#
+# This command will dump the dirty bitmap to a file by logging the
+# memory for a specified number of times with a defined time difference
+#
+# @filename: name of the file in which the bitmap will be saved.
+#
+# @iterations: #optional number of times the memory will be logged. The
+# min and max values are 3 and 100000 respectively. 3 is the default value,
+# if the iterations parameter is not stated.
+#
+# @period: #optional time difference in milliseconds between each iteration.
+# The min and max values are 10 and 100000 respectively. 10 is the default
+# value if the period parameter is not stated.
+#
+# Since 2.2
+##
+{ 'command' : 'log-dirty-bitmap',
+ 'data' : { 'filename' : 'str',
+ '*iterations' : 'int',
+ '*period' : 'int' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 7658d4b..db0a8ed 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3752,5 +3752,37 @@ Example:
-> { "execute": "rtc-reset-reinjection" }
<- { "return": {} }
+EQMP
+ {
+ .name = "log-dirty-bitmap",
+ .args_type = "filename:s,iterations:i?,period:i?",
+ .mhandler.cmd_new = qmp_marshal_input_log_dirty_bitmap,
+ },
+
+SQMP
+log-dirty-bitmap
+----------------
+
+start logging the memory of the VM for writable working set
+
+Arguments:
+
+- "filename": name of the file, in which the bitmap will be saved.
+
+- "iterations": #optional number of times, the memory will be logged.
+ The min and max values are 3 and 100000 respectively. 3 is the default value.
+
+- "period": #optional time difference in milliseconds between each iteration.
+ The min and max values are 10 and 100000 respectively. 10 is the default
+ value.
+
+Examples:
+-> { "execute": "log-dirty-bitmap",
+ "arguments": {
+ "filename": "/tmp/fileXXX",
+ "iterations": 3,
+ "period": 10 } }
+
+<- { "return": {} }
EQMP
diff --git a/savevm.c b/savevm.c
index e19ae0a..19f7b0c 100644
--- a/savevm.c
+++ b/savevm.c
@@ -42,6 +42,9 @@
#include "qemu/iov.h"
#include "block/snapshot.h"
#include "block/qapi.h"
+#include "exec/address-spaces.h"
+#include "exec/ram_addr.h"
+#include "qemu/bitmap.h"
#ifndef ETH_P_RARP
@@ -1137,6 +1140,381 @@ void do_savevm(Monitor *mon, const QDict *qdict)
}
}
+/*
+ * Adding the functionality of continuous logging of the
+ * dirty bitmap which is almost similar to the migration
+ * thread
+ */
+
+enum {
+ LOG_BITMAP_STATE_ERROR = -1,
+ LOG_BITMAP_STATE_NONE,
+ LOG_BITMAP_STATE_ACTIVE,
+ LOG_BITMAP_STATE_CANCELING,
+ LOG_BITMAP_STATE_COMPLETED
+};
+
+typedef struct BitmapLogState BitmapLogState;
+static int64_t MIN_ITERATION_VALUE = 3;
+static int64_t MIN_PERIOD_VALUE = 10;
+static int64_t MAX_ITERATION_VALUE = 100000;
+static int64_t MAX_PERIOD_VALUE = 100000;
+
+struct BitmapLogState {
+ int state;
+ int fd;
+ int64_t current_period;
+ int64_t current_iteration;
+ int64_t iterations;
+ unsigned long *log_bitmap_array;
+ QemuThread thread;
+};
+
+/*
+ * helper functions
+ */
+
+static inline void log_bitmap_lock(void)
+{
+ qemu_mutex_lock_iothread();
+ qemu_mutex_lock_ramlist();
+}
+
+static inline void log_bitmap_unlock(void)
+{
+ qemu_mutex_unlock_ramlist();
+ qemu_mutex_unlock_iothread();
+}
+
+static inline void log_bitmap_set_dirty(ram_addr_t addr,
+ unsigned long *log_bitmap_array)
+{
+ long nr = addr >> TARGET_PAGE_BITS;
+ set_bit(nr, log_bitmap_array);
+}
+
+static bool log_bitmap_set_status(BitmapLogState *b,
+ int old_state,
+ int new_state)
+{
+ return atomic_cmpxchg(&b->state, old_state, new_state);
+}
+
+/*
+ * inspired from migration mechanism
+ */
+
+static BitmapLogState *log_bitmap_get_current_state(void)
+{
+ static BitmapLogState current_bitmaplogstate = {
+ .state = LOG_BITMAP_STATE_NONE,
+ .log_bitmap_array = NULL,
+ };
+
+ return ¤t_bitmaplogstate;
+}
+
+/*
+ * syncing the log_bitmap with the ram_list dirty bitmap
+ */
+
+static void log_bitmap_dirty_bitmap_sync(unsigned long *log_bitmap_array)
+{
+ RAMBlock *block;
+ uint64_t counter = 0; /* 0 means log bitmap */
+ address_space_sync_dirty_bitmap(&address_space_memory);
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ qemu_bitmap_sync_range(block->mr->ram_addr, block->length,
+ log_bitmap_array, &counter);
+ }
+}
+
+static inline bool value_in_range(int64_t value, int64_t min_value,
+ int64_t max_value, const char *str,
+ Error **errp)
+{
+ if (value < min_value) {
+ error_setg(errp, "%s's value must be greater than %ld",
+ str, min_value);
+ return false;
+ }
+ if (value > max_value) {
+ error_setg(errp, "%s's value must be less than %ld",
+ str, max_value);
+ return false;
+ }
+ return true;
+}
+
+static inline void log_bitmap_close(BitmapLogState *b)
+{
+ log_bitmap_lock();
+ memory_global_dirty_log_stop();
+ log_bitmap_unlock();
+
+ g_free(b->log_bitmap_array);
+ b->log_bitmap_array = NULL;
+ qemu_close(b->fd);
+ b->fd = -1;
+}
+
+static bool log_bitmap_ram_block_info_dump(int fd, int64_t ram_bitmap_pages,
+ bool dump_blocks_info)
+{
+ int block_count = 0;
+ int block_name_length;
+ RAMBlock *block;
+ int ret;
+
+ if (qemu_write_full(fd, &ram_bitmap_pages, sizeof(int64_t)) < 0) {
+ return true;
+ }
+
+ if (dump_blocks_info) {
+
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ block_count++;
+ }
+
+ ret = qemu_write_full(fd, &block_count, sizeof(int));
+ if (ret < sizeof(int)) {
+ return true;
+ }
+
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ block_name_length = strlen(block->idstr) + 1;
+ ret = qemu_write_full(fd, &block_name_length, sizeof(int));
+ if (ret < sizeof(int)) {
+ return true;
+ }
+
+ ret = qemu_write_full(fd, &(block->idstr), sizeof(char) *
+ block_name_length);
+ if (ret < sizeof(char) * block_name_length) {
+ return true;
+ }
+
+ ret = qemu_write_full(fd, &(block->offset), sizeof(ram_addr_t));
+ if (ret < sizeof(ram_addr_t)) {
+ return true;
+ }
+
+ ret = qemu_write_full(fd, &(block->length), sizeof(ram_addr_t));
+ if (ret < sizeof(ram_addr_t)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void log_bitmap_update_status(BitmapLogState *b)
+{
+ int s = b->state;
+ switch (s) {
+ case LOG_BITMAP_STATE_ACTIVE:
+ case LOG_BITMAP_STATE_CANCELING:
+ case LOG_BITMAP_STATE_ERROR:
+ log_bitmap_set_status(b, s, LOG_BITMAP_STATE_COMPLETED);
+ }
+ return;
+}
+
+static void *bitmap_logging_thread(void *opaque)
+{
+ /*
+ * setup basic structures
+ */
+
+ BitmapLogState *b = opaque;
+ int fd = b->fd;
+ int64_t current_ram_bitmap_pages, prev_ram_bitmap_pages;
+ size_t bitmap_size = 0;
+ unsigned long *temp_log_bitmap_array = NULL;
+ char marker = 'M';
+ int ret;
+
+ b->current_iteration = 1;
+ log_bitmap_set_status(b, LOG_BITMAP_STATE_NONE,
+ LOG_BITMAP_STATE_ACTIVE);
+
+ current_ram_bitmap_pages = 0;
+ prev_ram_bitmap_pages = 1;
+
+ /*
+ * start the logging period
+ */
+
+ /*
+ * need lock for getting the information about the ram pages.
+ * This does not change on acquiring the lock
+ */
+ log_bitmap_lock();
+ current_ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
+ bitmap_size = BITS_TO_LONGS(current_ram_bitmap_pages) *
+ sizeof(unsigned long);
+ b->log_bitmap_array = bitmap_new(current_ram_bitmap_pages);
+ if (b->log_bitmap_array == NULL) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ log_bitmap_unlock();
+ goto log_thread_end;
+ }
+
+ memory_global_dirty_log_start();
+ log_bitmap_dirty_bitmap_sync(b->log_bitmap_array);
+ log_bitmap_unlock();
+
+ /*
+ * sync the dirty bitmap along with saving it
+ * using the QEMUFile pointer.
+ */
+ while (b->current_iteration <= b->iterations) {
+ if (!runstate_is_running() ||
+ b->state != LOG_BITMAP_STATE_ACTIVE) {
+ goto log_thread_end;
+ }
+
+ /*
+ * Need to calculate the ram pages again as there is a
+ * possibility of the change in the memory
+ */
+ log_bitmap_lock();
+ current_ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
+ if (current_ram_bitmap_pages != prev_ram_bitmap_pages) {
+ temp_log_bitmap_array = bitmap_new(current_ram_bitmap_pages);
+ if (temp_log_bitmap_array == NULL) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ log_bitmap_unlock();
+ goto log_thread_end;
+ }
+ log_bitmap_dirty_bitmap_sync(temp_log_bitmap_array);
+ } else {
+ log_bitmap_dirty_bitmap_sync(b->log_bitmap_array);
+ }
+ log_bitmap_unlock();
+
+ if (current_ram_bitmap_pages != prev_ram_bitmap_pages) {
+ prev_ram_bitmap_pages = current_ram_bitmap_pages;
+ bitmap_size = BITS_TO_LONGS(current_ram_bitmap_pages) *
+ sizeof(unsigned long);
+ if (b->log_bitmap_array) {
+ g_free(b->log_bitmap_array);
+ }
+ b->log_bitmap_array = temp_log_bitmap_array;
+ temp_log_bitmap_array = NULL;
+ if (log_bitmap_ram_block_info_dump(fd, current_ram_bitmap_pages,
+ true)) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+ } else {
+ if (log_bitmap_ram_block_info_dump(fd, current_ram_bitmap_pages,
+ false)) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+ }
+
+ ret = qemu_write_full(fd, b->log_bitmap_array, bitmap_size);
+ if (ret < bitmap_size) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+
+ ret = qemu_write_full(fd, &marker, sizeof(char));
+ if (ret < sizeof(char)) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+ g_usleep(b->current_period * 1000);
+ b->current_iteration++;
+ bitmap_zero(b->log_bitmap_array, current_ram_bitmap_pages);
+ }
+
+ /*
+ * stop the logging period.
+ */
+ log_thread_end:
+ log_bitmap_close(b);
+ log_bitmap_update_status(b);
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_NONE);
+ return NULL;
+}
+
+void qmp_log_dirty_bitmap(const char *filename, bool has_iterations,
+ int64_t iterations, bool has_period,
+ int64_t period, Error **errp)
+{
+ int fd = -1;
+ BitmapLogState *b = log_bitmap_get_current_state();
+ Error *local_err = NULL;
+
+ if (!runstate_is_running()) {
+ error_setg(errp, "Guest is not in a running state");
+ return;
+ }
+
+ if (!qemu_process_check(QEMU_DIRTY_BITMAP_USER_NONE) &&
+ !qemu_process_check(QEMU_DIRTY_BITMAP_USER_BITMAP_DUMP)) {
+ error_setg(errp, "Dirty bitmap dumping not possible, since %s "
+ "is in progress.", get_qemu_dirty_bitmap_user_as_string());
+ return;
+ }
+
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_BITMAP_DUMP);
+
+ if (b->state == LOG_BITMAP_STATE_ACTIVE ||
+ b->state == LOG_BITMAP_STATE_CANCELING) {
+ error_setg(errp, "dirty bitmap dump in progress");
+ return;
+ }
+
+ b->state = LOG_BITMAP_STATE_NONE;
+
+ /*
+ * checking the iteration range
+ */
+ if (!has_iterations) {
+ b->iterations = MIN_ITERATION_VALUE;
+ } else if (!value_in_range(iterations, MIN_ITERATION_VALUE,
+ MAX_ITERATION_VALUE, "iterations", &local_err))
{
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ return;
+ } else {
+ b->iterations = iterations;
+ }
+
+ /*
+ * checking the period range
+ */
+ if (!has_period) {
+ b->current_period = MIN_PERIOD_VALUE;
+ } else if (!value_in_range(period, MIN_PERIOD_VALUE,
+ MAX_PERIOD_VALUE, "period", &local_err)) {
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ return;
+ } else {
+ b->current_period = period;
+ }
+
+ fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
+ if (fd < 0) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ b->fd = fd;
+ qemu_thread_create(&b->thread, "dirty-bitmap-dump",
+ bitmap_logging_thread, b,
+ QEMU_THREAD_JOINABLE);
+
+ return;
+}
+
void qmp_xen_save_devices_state(const char *filename, Error **errp)
{
QEMUFile *f;
diff --git a/vl.c b/vl.c
index 95be92d..a858752 100644
--- a/vl.c
+++ b/vl.c
@@ -206,6 +206,8 @@ bool qemu_uuid_set;
static QEMUBootSetHandler *boot_set_handler;
static void *boot_set_opaque;
+int dirty_bitmap_user;
+
static NotifierList exit_notifiers =
NOTIFIER_LIST_INITIALIZER(exit_notifiers);
@@ -768,7 +770,27 @@ void vm_start(void)
qapi_event_send_resume(&error_abort);
}
+/*
+ * A global variable to decide which process will only
+ * execute migration or bitmap dump
+ */
+
+static QemuDirtyBitmapUser dbu = QEMU_DIRTY_BITMAP_USER_NONE;
+
+bool qemu_process_check(QemuDirtyBitmapUser user)
+{
+ return user == dbu;
+}
+
+void qemu_process_set(QemuDirtyBitmapUser user)
+{
+ dbu = user;
+}
+const char *get_qemu_dirty_bitmap_user_as_string(void)
+{
+ return QemuDirtyBitmapUser_lookup[dbu];
+}
/***********************************************************/
/* real time host monotonic timer */
@@ -4545,6 +4567,7 @@ int main(int argc, char **argv, char **envp)
}
if (incoming) {
+ qemu_process_set(QEMU_DIRTY_BITMAP_USER_MIGRATION);
Error *local_err = NULL;
qemu_start_incoming_migration(incoming, &local_err);
if (local_err) {
--
1.9.1
- [Qemu-devel] [PATCH v6 0/6] Obtain dirty bitmap via VM logging, Sanidhya Kashyap, 2014/09/13
- [Qemu-devel] [PATCH v6 2/6] BitmapLog: bitmap dump code,
Sanidhya Kashyap <=
- [Qemu-devel] [PATCH v6 4/6] BitmapLog: cancel mechanism for an already running dump bitmap process, Sanidhya Kashyap, 2014/09/13
- [Qemu-devel] [PATCH v6 5/6] BitmapLog: set the period of the dump bitmap process, Sanidhya Kashyap, 2014/09/13
- [Qemu-devel] [PATCH v6 3/6] BitmapLog: get the information about the parameters, Sanidhya Kashyap, 2014/09/13
- [Qemu-devel] [PATCH v6 6/6] BitmapLog: python script for extracting bitmap from a binary file, Sanidhya Kashyap, 2014/09/13
- [Qemu-devel] [PATCH v6 1/6] generic function between migration and bitmap dump, Sanidhya Kashyap, 2014/09/13