From patchwork Thu Jun 12 11:47:12 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sanidhya Kashyap X-Patchwork-Id: 359085 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id E639014009E for ; Thu, 12 Jun 2014 21:49:22 +1000 (EST) Received: from localhost ([::1]:53359 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wv3VQ-0006NQ-CA for incoming@patchwork.ozlabs.org; Thu, 12 Jun 2014 07:49:20 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43351) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wv3Uy-0006MP-Mv for qemu-devel@nongnu.org; Thu, 12 Jun 2014 07:49:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Wv3Up-0001Zs-JQ for qemu-devel@nongnu.org; Thu, 12 Jun 2014 07:48:52 -0400 Received: from mail-pd0-x229.google.com ([2607:f8b0:400e:c02::229]:52140) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wv3Up-0001Zn-7P for qemu-devel@nongnu.org; Thu, 12 Jun 2014 07:48:43 -0400 Received: by mail-pd0-f169.google.com with SMTP id w10so895414pde.28 for ; Thu, 12 Jun 2014 04:48:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=mXUfvzgfMXllpajpzjLzlJWu1hd/oFeObkrwyawJf3Y=; b=obi8zGXwlAotaMBvKv4eb/3kywfbJWVjFLFalvnkZDDMVPknnspBLp4EqyhGIoxq67 sME4aKChn+1AzmfOrnWO1MpkNNrB+KZzDKy9DpflsXa7z1O3/8JLop0SLhDCFW82qOwx gRoEypZpMdhxK/KY6D5x3FSYkAca2qzSxSx4mMFD+Q/EDAF68mG+hG/4JH+XHU1hTwPR FoCk8a8OWl+tMIhCTVRoD+GAoB8kZWbDZhQJvkYxQAN6d4HK39+Kz4wej/tnn5H66A6G wK0LJE5sxHYSGXxqoqa/Msmat63EzsnfAPelKUU8GvEW9kWxZVa459sTDvGnVDODPiuh 9lXA== X-Received: by 10.66.158.132 with SMTP id wu4mr20779888pab.66.1402573722244; Thu, 12 Jun 2014 04:48:42 -0700 (PDT) Received: from h311.iiit.ac.in ([14.139.82.6]) by mx.google.com with ESMTPSA id jd5sm80123913pbb.18.2014.06.12.04.48.39 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 12 Jun 2014 04:48:41 -0700 (PDT) From: Sanidhya Kashyap To: qemu list Date: Thu, 12 Jun 2014 17:17:12 +0530 Message-Id: <1402573632-17447-1-git-send-email-sanidhya.iiith@gmail.com> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1402569400-12242-1-git-send-email-sanidhya.iiith@gmail.com> References: <1402569400-12242-1-git-send-email-sanidhya.iiith@gmail.com> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400e:c02::229 Cc: Sanidhya Kashyap , "Dr. David Alan Gilbert" , Juan Quintela Subject: [Qemu-devel] [PATCH v3 3/7][RESEND PATCH] bitmap dump code via QAPI framework with runstates X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- include/exec/cpu-all.h | 3 +- migration.c | 7 ++ qapi-schema.json | 18 +++ qmp-commands.hx | 33 +++++ savevm.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 385 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..23ba44f 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,328 @@ 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)) { + return true; + } + + 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;