From: Sanidhya Kashyap <sanidhya.iiith@gmail.com>
To: qemu list <qemu-devel@nongnu.org>
Cc: Sanidhya Kashyap <sanidhya.iiith@gmail.com>,
"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
Juan Quintela <quintela@redhat.com>
Subject: [Qemu-devel] [PATCH v3 3/7] bitmap dump code via QAPI framework with runstates
Date: Thu, 12 Jun 2014 16:06:36 +0530 [thread overview]
Message-ID: <1402569400-12242-4-git-send-email-sanidhya.iiith@gmail.com> (raw)
In-Reply-To: <1402569400-12242-1-git-send-email-sanidhya.iiith@gmail.com>
Reformatted the code and added the functionality of dumping the blocks' info
which can be read by the user when required. I have also made the block name
length global.
Signed-off-by: Sanidhya Kashyap <sanidhya.iiith@gmail.com>
---
include/exec/cpu-all.h | 3 +-
migration.c | 7 ++
qapi-schema.json | 18 +++
qmp-commands.hx | 33 +++++
savevm.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 384 insertions(+), 1 deletion(-)
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index e8363d7..9e6d903 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -301,6 +301,7 @@ extern ram_addr_t ram_size;
/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
#define RAM_PREALLOC_MASK (1 << 0)
+#define RAMBLOCK_NAME_LENGTH (1<<8)
typedef struct RAMBlock {
struct MemoryRegion *mr;
@@ -308,7 +309,7 @@ typedef struct RAMBlock {
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/migration.c b/migration.c
index 3fc03d6..d91dd4c 100644
--- a/migration.c
+++ b/migration.c
@@ -436,6 +436,13 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
return;
}
+ if (runstate_check(RUN_STATE_DUMP_BITMAP)) {
+ error_setg(errp, "bitmap dump in progress");
+ return;
+ }
+
+ runstate_set(RUN_STATE_MIGRATE);
+
s = migrate_init(¶ms);
if (strstart(uri, "tcp:", &p)) {
diff --git a/qapi-schema.json b/qapi-schema.json
index f9c75f9..aa78540 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3082,3 +3082,21 @@
'btn' : 'InputBtnEvent',
'rel' : 'InputMoveEvent',
'abs' : 'InputMoveEvent' } }
+
+##
+# @log-dirty-bitmap
+#
+# dumps the dirty bitmap to a file by logging the
+# memory for a specified number of times with a
+# a defined time differnce
+#
+# @filename: name of the file in which the bitmap will be saved.
+# @epochs: number of times the memory will be logged.
+# @frequency: time difference in milliseconds between each epoch.
+#
+# Since 2.1
+##
+{ 'command' : 'log-dirty-bitmap',
+ 'data' : { 'filename' : 'str',
+ '*epochs' : 'int',
+ '*frequency' : 'int' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index d8aa4ed..183a636 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3572,3 +3572,36 @@ Example:
} } ] }
EQMP
+
+ {
+ .name = "log-dirty-bitmap",
+ .args_type = "filename:s,epochs:i?,frequency:i?,readable:-r?",
+ .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
+- "epochs": number of times, the memory will be logged
+- "frequency": time difference in milliseconds between each epoch
+
+Examples:
+-> { "execute" : "log-dirty-bitmap",
+ "arguments" : {
+ "filename" : "/tmp/fileXXX",
+ "epochs" : 3,
+ "frequency" : 10 } }
+
+<- { "return": {} }
+
+Note: The epochs, frequency and readable are optional. epochs default
+value is 3 while that of frequency is 10.
+
+EQMP
+
diff --git a/savevm.c b/savevm.c
index da8aa24..40b6caf 100644
--- a/savevm.c
+++ b/savevm.c
@@ -41,6 +41,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"
#define SELF_ANNOUNCE_ROUNDS 5
@@ -1002,6 +1005,327 @@ 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_SETUP,
+ LOG_BITMAP_STATE_ACTIVE,
+ LOG_BITMAP_STATE_CANCELING,
+ LOG_BITMAP_STATE_COMPLETED
+};
+
+typedef struct BitmapLogState BitmapLogState;
+static unsigned long *logging_bitmap;
+static int64_t MIN_EPOCH_VALUE = 3;
+static int64_t MIN_FREQUENCY_VALUE = 10;
+static int64_t MAX_EPOCH_VALUE = 100000;
+static int64_t MAX_FREQUENCY_VALUE = 100000;
+
+struct BitmapLogState {
+ int state;
+ int fd;
+ int64_t current_frequency;
+ int64_t total_epochs;
+ QemuThread thread;
+};
+
+/*
+ * helper functions
+ */
+
+static inline void logging_lock(void)
+{
+ qemu_mutex_lock_iothread();
+ qemu_mutex_lock_ramlist();
+}
+
+static inline void logging_unlock(void)
+{
+ qemu_mutex_unlock_ramlist();
+ qemu_mutex_unlock_iothread();
+}
+
+static inline void logging_bitmap_set_dirty(ram_addr_t addr)
+{
+ int nr = addr >> TARGET_PAGE_BITS;
+ set_bit(nr, logging_bitmap);
+}
+
+static bool logging_state_set_status(BitmapLogState *b,
+ int old_state,
+ int new_state)
+{
+ return atomic_cmpxchg(&b->state, old_state, new_state);
+}
+
+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;
+}
+
+/*
+ * inspired from migration mechanism
+ */
+
+static BitmapLogState *logging_current_state(void)
+{
+ static BitmapLogState current_bitmaplogstate = {
+ .state = LOG_BITMAP_STATE_NONE,
+ };
+
+ return ¤t_bitmaplogstate;
+}
+
+/*
+ * syncing the logging_bitmap with the ram_list dirty bitmap
+ */
+
+static void dirty_bitmap_sync(void)
+{
+ RAMBlock *block;
+ 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,
+ logging_bitmap, false);
+ }
+}
+
+static inline void logging_bitmap_close(BitmapLogState *b)
+{
+ logging_lock();
+ memory_global_dirty_log_stop();
+ logging_unlock();
+
+ g_free(logging_bitmap);
+ logging_bitmap = NULL;
+ qemu_close(b->fd);
+ b->fd = -1;
+}
+
+static bool ram_block_info_dump(int fd)
+{
+ int64_t ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
+ int block_count = 0;
+ RAMBlock *block;
+ int ret;
+
+ if (qemu_write_full(fd, &ram_bitmap_pages, sizeof(int64_t)) < 0) {
+ return true;
+ }
+
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ block_count++;
+ }
+
+ ret = qemu_write_full(fd, &block_count, sizeof(int));
+ if (ret < sizeof(int)) {
+ }
+
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ ret = qemu_write_full(fd, &(block->idstr), sizeof(char) *
+ RAMBLOCK_NAME_LENGTH);
+ if (ret < sizeof(char) * RAMBLOCK_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 logging_state_update_status(BitmapLogState *b)
+{
+ switch (b->state) {
+ case LOG_BITMAP_STATE_ACTIVE:
+ logging_state_set_status(b, LOG_BITMAP_STATE_ACTIVE,
+ LOG_BITMAP_STATE_COMPLETED);
+ return;
+ case LOG_BITMAP_STATE_CANCELING:
+ logging_state_set_status(b, LOG_BITMAP_STATE_CANCELING,
+ LOG_BITMAP_STATE_COMPLETED);
+ return;
+ case LOG_BITMAP_STATE_ERROR:
+ logging_state_set_status(b, LOG_BITMAP_STATE_ERROR,
+ LOG_BITMAP_STATE_COMPLETED);
+ }
+ return;
+}
+
+static void *bitmap_logging_thread(void *opaque)
+{
+ /*
+ * setup basic structures
+ */
+
+ BitmapLogState *b = opaque;
+ int fd = b->fd;
+ int64_t epoch_count = 0;
+ int64_t total_epochs = b->total_epochs;
+ int64_t ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
+ size_t bitmap_size = BITS_TO_LONGS(ram_bitmap_pages) *
+ sizeof(unsigned long);
+ int ret;
+ char marker = 'M';
+
+ logging_state_set_status(b, LOG_BITMAP_STATE_NONE,
+ LOG_BITMAP_STATE_SETUP);
+
+ logging_bitmap = bitmap_new(ram_bitmap_pages);
+
+ if (logging_bitmap == NULL) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+
+ logging_state_set_status(b, LOG_BITMAP_STATE_SETUP,
+ LOG_BITMAP_STATE_ACTIVE);
+ /*
+ * start the logging period
+ */
+ logging_lock();
+ memory_global_dirty_log_start();
+ dirty_bitmap_sync();
+ bitmap_zero(logging_bitmap, ram_bitmap_pages);
+ logging_unlock();
+
+ if (ram_block_info_dump(fd)) {
+ b->state = LOG_BITMAP_STATE_ERROR;
+ goto log_thread_end;
+ }
+
+ /*
+ * sync the dirty bitmap along with saving it
+ * using the FILE pointer f.
+ */
+ while (epoch_count < total_epochs) {
+ if (!runstate_check(RUN_STATE_DUMP_BITMAP) ||
+ b->state != LOG_BITMAP_STATE_ACTIVE) {
+ goto log_thread_end;
+ }
+ bitmap_zero(logging_bitmap, ram_bitmap_pages);
+ logging_lock();
+ dirty_bitmap_sync();
+ logging_unlock();
+
+ ret = qemu_write_full(fd, logging_bitmap, 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_frequency * 1000);
+ epoch_count++;
+ }
+
+ /*
+ * stop the logging period.
+ */
+ log_thread_end:
+ logging_bitmap_close(b);
+ logging_state_update_status(b);
+ runstate_set(RUN_STATE_RUNNING);
+ return NULL;
+}
+
+void qmp_log_dirty_bitmap(const char *filename, bool has_epochs,
+ int64_t epochs, bool has_frequency,
+ int64_t frequency, Error **errp)
+{
+ int fd = -1;
+ BitmapLogState *b = logging_current_state();
+ Error *local_err = NULL;
+ if (runstate_check(RUN_STATE_DUMP_BITMAP) ||
+ b->state == LOG_BITMAP_STATE_ACTIVE ||
+ b->state == LOG_BITMAP_STATE_SETUP ||
+ b->state == LOG_BITMAP_STATE_CANCELING) {
+ error_setg(errp, "dirty bitmap dump in progress");
+ return;
+ }
+
+ if (!runstate_is_running()) {
+ error_setg(errp, "Guest is not in a running state");
+ return;
+ }
+
+ runstate_set(RUN_STATE_DUMP_BITMAP);
+ b->state = LOG_BITMAP_STATE_NONE;
+
+ /*
+ * checking the epoch range
+ */
+ if (!has_epochs) {
+ b->total_epochs = MIN_EPOCH_VALUE;
+ } else if (!value_in_range(epochs, MIN_EPOCH_VALUE,
+ MAX_EPOCH_VALUE, "epoch", &local_err)) {
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ runstate_set(RUN_STATE_RUNNING);
+ return;
+ } else {
+ b->total_epochs = epochs;
+ }
+
+ /*
+ * checking the frequency range
+ */
+ if (!has_frequency) {
+ b->current_frequency = MIN_FREQUENCY_VALUE;
+ } else if(!value_in_range(frequency, MIN_FREQUENCY_VALUE,
+ MAX_FREQUENCY_VALUE, "frequency", &local_err)) {
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ runstate_set(RUN_STATE_RUNNING);
+ return;
+ } else {
+ b->current_frequency = frequency;
+ }
+
+ fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
+ if (fd < 0) {
+ error_setg_file_open(errp, errno, filename);
+ runstate_set(RUN_STATE_RUNNING);
+ 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;
--
1.9.3
next prev parent reply other threads:[~2014-06-12 10:38 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-12 10:36 [Qemu-devel] [PATCH v3 0/7] Obtain dirty bitmap via VM logging Sanidhya Kashyap
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 1/7] enable sharing of the function between migration and bitmap dump Sanidhya Kashyap
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 2/7] RunState: added two new flags for bitmap dump and migration process Sanidhya Kashyap
2014-06-12 11:17 ` Sanidhya Kashyap
2014-06-12 10:36 ` Sanidhya Kashyap [this message]
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 4/7] hmp interface for dirty bitmap dump Sanidhya Kashyap
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 5/7] cancel mechanism for an already running dump bitmap process Sanidhya Kashyap
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 6/7] set the frequency of the " Sanidhya Kashyap
2014-06-12 10:36 ` [Qemu-devel] [PATCH v3 7/7] python script for extracting bitmap from a binary file Sanidhya Kashyap
2014-06-12 11:47 ` [Qemu-devel] [PATCH v3 3/7][RESEND PATCH] bitmap dump code via QAPI framework with runstates Sanidhya Kashyap
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1402569400-12242-4-git-send-email-sanidhya.iiith@gmail.com \
--to=sanidhya.iiith@gmail.com \
--cc=dgilbert@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.