All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: Sanidhya Kashyap <sanidhya.iiith@gmail.com>
Cc: Amit Shah <amit.shah@redhat.com>,
	qemu list <qemu-devel@nongnu.org>,
	Juan Quintela <quintela@redhat.com>
Subject: Re: [Qemu-devel] [PATCH v4 3/8] BitmapLog: bitmap dump code via QAPI framework with runstates
Date: Fri, 18 Jul 2014 12:12:29 +0100	[thread overview]
Message-ID: <20140718111228.GE2384@work-vm> (raw)
In-Reply-To: <1405596081-29701-4-git-send-email-sanidhya.iiith@gmail.com>

* Sanidhya Kashyap (sanidhya.iiith@gmail.com) wrote:
> 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.
> Some minor modification to the structure which is now storing all the
> information.

One thought, I wonder how much of the savevm.c changes could move into
a separate file rather than making savevm even bigger?

> 
> Signed-off-by: Sanidhya Kashyap <sanidhya.iiith@gmail.com>
> ---
>  include/exec/cpu-all.h |   4 +-
>  migration.c            |   7 ++
>  qapi-schema.json       |  18 +++
>  qmp-commands.hx        |  30 +++++
>  savevm.c               | 325 +++++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 383 insertions(+), 1 deletion(-)
> 
> diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
> index f91581f..b459301 100644
> --- a/include/exec/cpu-all.h
> +++ b/include/exec/cpu-all.h
> @@ -297,13 +297,15 @@ CPUArchState *cpu_copy(CPUArchState *env);
>  
>  /* memory API */
>  
> +#define RAMBLOCK_NAME_LENGTH (1<<8)

Be careful; making this bigger would break migration formats,
making it smaller would probably break migration loading.

>  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/migration.c b/migration.c
> index 8d675b3..e2e313c 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(&params);
>  
>      if (strstart(uri, "tcp:", &p)) {
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 501b8d0..924d6bc 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3485,3 +3485,21 @@
>  # Since: 2.1
>  ##
>  { 'command': 'rtc-reset-reinjection' }
> +
> +##
> +# @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 (optional).
> +# @frequency: time difference in milliseconds between each epoch (optional).
> +#
> +# Since 2.2
> +##
> +{ 'command' : 'log-dirty-bitmap',
> +  'data'    : { 'filename'      : 'str',
> +                '*epochs'       : 'int',
> +                '*frequency'    : 'int' } }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 4be4765..200f57e 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -3753,5 +3753,35 @@ Example:
>  
>  -> { "execute": "rtc-reset-reinjection" }
>  <- { "return": {} }
> +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 e19ae0a..ecb334e 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,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
> +};

I'd be tempted to give that enum a name and use it as the type
for functions that pass the state around (although I realise
you have to be careful with the variable having to be an int
for the atomic).

> +
> +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 current_epoch;
> +    int64_t total_epochs;
> +    QemuThread thread;
> +};
> +
> +/*
> + * helper functions
> + */
> +
> +static inline void logging_lock(void)
> +{
> +    qemu_mutex_lock_iothread();
> +    qemu_mutex_lock_ramlist();
> +}

I wonder how often you can really not have the ramlist locked; if stuff
is added/removed the last_ram_offset would change, so to really be safe
you probably need to hold it for much longer than you currently do -
but that might not be practical.

> +
> +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;

Be careful; int is too small; long is probably what's
needed (which I think is the type of the parameter to set_bit).

> +    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;
> +}

This seems a pretty generic function; could it go somewhere
in util or the like?

> +
> +/*
> + * inspired from migration mechanism
> + */
> +
> +static BitmapLogState *logging_current_state(void)
> +{
> +    static BitmapLogState current_bitmaplogstate = {
> +        .state = LOG_BITMAP_STATE_NONE,
> +    };
> +
> +    return &current_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);
> +    }
> +}

I think that should be logging_dirty_bitmap_sync since it's
specific to logging_bitmap.

> +
> +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;
> +}

I didn't really see the point of this at first, but I guess
it always moves to 'COMPLETED' unless you're already at completed
or in NONE; but then perhaps:

    int s = b->state;
    switch (s) {
    case LOG_BITMAP_STATE_ACTIVE:
    case LOG_BITMAP_STATE_CANCELING:
    case LOG_BITMAP_STATE_ERROR:
       logging_state_set_status(b, s,
                                   LOG_BITMAP_STATE_COMPLETED);
       return;
   }
   return;

would be more obvious (note I only read the state once)

Dave

> +static void *bitmap_logging_thread(void *opaque)
> +{
> +    /*
> +     * setup basic structures
> +     */
> +
> +    BitmapLogState *b = opaque;
> +    int fd = b->fd;
> +    b->current_epoch = 1;
> +    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 (b->current_epoch <= b->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);
> +        b->current_epoch++;
> +    }
> +
> +    /*
> +     * 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
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

  reply	other threads:[~2014-07-18 11:12 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-17 11:21 [Qemu-devel] [PATCH v4 0/8] Obtain dirty bitmap via VM logging Sanidhya Kashyap
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 1/8] enable sharing of the function between migration and bitmap dump Sanidhya Kashyap
2014-07-18 11:00   ` Dr. David Alan Gilbert
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 2/8] RunState: added two new flags for bitmap dump and migration process Sanidhya Kashyap
2014-07-18 11:02   ` Dr. David Alan Gilbert
2014-07-18 12:16   ` Eric Blake
2014-07-18 18:01     ` Sanidhya Kashyap
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 3/8] BitmapLog: bitmap dump code via QAPI framework with runstates Sanidhya Kashyap
2014-07-18 11:12   ` Dr. David Alan Gilbert [this message]
2014-07-18 18:18     ` Sanidhya Kashyap
2014-07-18 11:14   ` Dr. David Alan Gilbert
2014-07-18 18:09     ` Sanidhya Kashyap
2014-07-18 12:20   ` Eric Blake
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 4/8] BitmapLog: hmp interface for dirty bitmap dump Sanidhya Kashyap
2014-07-18 11:15   ` Dr. David Alan Gilbert
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 5/8] BitmapLog: cancel mechanism for an already running dump bitmap process Sanidhya Kashyap
2014-07-18 12:22   ` Eric Blake
2014-07-18 17:51     ` Sanidhya Kashyap
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 6/8] BitmapLog: set the frequency of the " Sanidhya Kashyap
2014-07-18 12:28   ` Eric Blake
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 7/8] BitmapLog: get the information about the parameters Sanidhya Kashyap
2014-07-18 12:35   ` Eric Blake
2014-07-18 17:41     ` Sanidhya Kashyap
2014-07-17 11:21 ` [Qemu-devel] [PATCH v4 8/8] BitmapLog: python script for extracting bitmap from a binary file Sanidhya Kashyap
2014-07-18 11:17   ` Dr. David Alan Gilbert
2014-07-18 10:56 ` [Qemu-devel] [PATCH v4 0/8] Obtain dirty bitmap via VM logging Dr. David Alan Gilbert
2014-07-18 13:42   ` Eric Blake
2014-07-18 17:28   ` Sanidhya Kashyap
2014-07-18 17:42     ` Dr. David Alan Gilbert
2014-07-18 12:39 ` Eric Blake

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=20140718111228.GE2384@work-vm \
    --to=dgilbert@redhat.com \
    --cc=amit.shah@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    --cc=sanidhya.iiith@gmail.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.