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.ii...@gmail.com> --- 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; -- 1.9.3