Following are the changes made with respect to the previous version: Chen's advice 1) Replaced DIRTY_MEMORY_LOG_BITMAP with DIRTY_MEMORY_MIGRATION and completely removed the DIRTY_MEMORY_LOG_BITMAP flag.
Eric's advice 2) Replaced FILE pointer with file descriptor. 3) Replaced fopen/fclose with qemu_open / qemu_close. 4) Removed text format, output only in machine-readable format. 5) Defined constants. Signed-off-by: Sanidhya Kashyap <sanidhya.ii...@gmail.com> --- qapi-schema.json | 17 ++++ qmp-commands.hx | 33 ++++++++ savevm.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) diff --git a/qapi-schema.json b/qapi-schema.json index 7bc33ea..17e5147 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4722,3 +4722,20 @@ '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..525b388 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,249 @@ 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 LOG_SIZE_MAX = 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 check_value(int64_t value, int64_t min_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 > LOG_SIZE_MAX) { + error_setg(errp, "%s's value must be less than %ld", + str, LOG_SIZE_MAX); + 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) { + 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); + qemu_close(b->fd); + logging_bitmap = NULL; + b = NULL; +} + +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); + + 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 (qemu_write_full(fd, &ram_bitmap_pages, sizeof(int64_t)) < 0) { + 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_is_running() || b->state != LOG_BITMAP_STATE_ACTIVE) { + goto log_thread_end; + } + bitmap_zero(logging_bitmap, ram_bitmap_pages); + logging_lock(); + dirty_bitmap_sync(); + logging_unlock(); + if (qemu_write_full(fd, logging_bitmap, bitmap_size) < 0) { + 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); + if (b->state == LOG_BITMAP_STATE_ACTIVE) { + logging_state_set_status(b, LOG_BITMAP_STATE_ACTIVE, + LOG_BITMAP_STATE_COMPLETED); + } else if (b->state == LOG_BITMAP_STATE_CANCELING) { + logging_state_set_status(b, LOG_BITMAP_STATE_CANCELING, + LOG_BITMAP_STATE_COMPLETED); + } else if (b->state == LOG_BITMAP_STATE_ERROR) { + logging_state_set_status(b, LOG_BITMAP_STATE_ERROR, + LOG_BITMAP_STATE_COMPLETED); + } + 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 (b->state == LOG_BITMAP_STATE_ACTIVE || + b->state == LOG_BITMAP_STATE_SETUP || + b->state == LOG_BITMAP_STATE_CANCELING) { + b = NULL; + error_setg(errp, "dirty bitmap dump in progress"); + return; + } + + if (b->state == LOG_BITMAP_STATE_COMPLETED) { + b->state = LOG_BITMAP_STATE_NONE; + } + + if (!has_epochs) { + epochs = MIN_EPOCH_VALUE; + } + if (!has_frequency) { + frequency = MIN_FREQUENCY_VALUE; + } + + if (!check_value(epochs, MIN_EPOCH_VALUE, "epoch", &local_err) || + !check_value(frequency, MIN_FREQUENCY_VALUE, "frequency", &local_err)) { + if (local_err) { + b = NULL; + error_propagate(errp, local_err); + return; + } + } + + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); + if (fd < 0) { + error_setg_file_open(errp, errno, filename); + b = NULL; + return; + } + + b->total_epochs = epochs; + b->current_frequency = frequency; + 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.8.3.1