From: "Wang, Lei" <lei4.wang@intel.com>
To: Andrei Gudkov <gudkov.andrei@huawei.com>, qemu-devel@nongnu.org
Cc: quintela@redhat.com, eblake@redhat.com, armbru@redhat.com,
berrange@redhat.com, zhengchuan@huawei.com
Subject: Re: [PATCH v2 2/4] migration/calc-dirty-rate: detailed stats in sampling mode
Date: Tue, 30 May 2023 11:06:30 +0800 [thread overview]
Message-ID: <40cc401c-a9c7-aca7-d80e-92ece193bb35@intel.com> (raw)
In-Reply-To: <22436421241c49c9b6d9b9120d166392c40fb991.1682598010.git.gudkov.andrei@huawei.com>
On 4/27/2023 20:42, Andrei Gudkov via wrote:
> Collect number of dirty pages for progresseively increasing time
> periods starting with 125ms up to number of seconds specified with
> calc-dirty-rate. Report through qmp and hmp: 1) vector of dirty page
> measurements, 2) page size, 3) total number of VM pages, 4) number
> of sampled pages.
>
> Signed-off-by: Andrei Gudkov <gudkov.andrei@huawei.com>
> ---
> migration/dirtyrate.c | 165 +++++++++++++++++++++++++++++-------------
> migration/dirtyrate.h | 25 ++++++-
> qapi/migration.json | 24 +++++-
> 3 files changed, 160 insertions(+), 54 deletions(-)
>
> diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c
> index acba3213a3..4491bbe91a 100644
> --- a/migration/dirtyrate.c
> +++ b/migration/dirtyrate.c
> @@ -224,6 +224,7 @@ static struct DirtyRateInfo *query_dirty_rate_info(void)
> info->calc_time = DirtyStat.calc_time;
> info->sample_pages = DirtyStat.sample_pages;
> info->mode = dirtyrate_mode;
> + info->page_size = TARGET_PAGE_SIZE;
>
> if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
> info->has_dirty_rate = true;
> @@ -245,6 +246,29 @@ static struct DirtyRateInfo *query_dirty_rate_info(void)
> info->vcpu_dirty_rate = head;
> }
>
> + if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
> + int64List *periods_head = NULL;
> + int64List **periods_tail = &periods_head;
> + int64List *n_dirty_pages_head = NULL;
> + int64List **n_dirty_pages_tail = &n_dirty_pages_head;
> +
> + info->n_total_pages = DirtyStat.page_sampling.n_total_pages;
> + info->has_n_total_pages = true;
> +
> + info->n_sampled_pages = DirtyStat.page_sampling.n_sampled_pages;
> + info->has_n_sampled_pages = true;
> +
> + for (i = 0; i < DirtyStat.page_sampling.n_readings; i++) {
> + DirtyReading *dr = &DirtyStat.page_sampling.readings[i];
> + QAPI_LIST_APPEND(periods_tail, dr->period);
> + QAPI_LIST_APPEND(n_dirty_pages_tail, dr->n_dirty_pages);
> + }
> + info->n_dirty_pages = n_dirty_pages_head;
> + info->periods = periods_head;
> + info->has_n_dirty_pages = true;
> + info->has_periods = true;
> + }
> +
> if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
> info->sample_pages = 0;
> }
> @@ -265,9 +289,11 @@ static void init_dirtyrate_stat(int64_t start_time,
>
> switch (config.mode) {
> case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING:
> - DirtyStat.page_sampling.total_dirty_samples = 0;
> - DirtyStat.page_sampling.total_sample_count = 0;
> - DirtyStat.page_sampling.total_block_mem_MB = 0;
> + DirtyStat.page_sampling.n_total_pages = 0;
> + DirtyStat.page_sampling.n_sampled_pages = 0;
> + DirtyStat.page_sampling.n_readings = 0;
> + DirtyStat.page_sampling.readings = g_try_malloc0_n(MAX_DIRTY_READINGS,
> + sizeof(DirtyReading));
> break;
> case DIRTY_RATE_MEASURE_MODE_DIRTY_RING:
> DirtyStat.dirty_ring.nvcpu = -1;
> @@ -285,28 +311,10 @@ static void cleanup_dirtyrate_stat(struct DirtyRateConfig config)
> free(DirtyStat.dirty_ring.rates);
> DirtyStat.dirty_ring.rates = NULL;
> }
> -}
> -
> -static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
> -{
> - DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count;
> - DirtyStat.page_sampling.total_sample_count += info->sample_pages_count;
> - /* size of total pages in MB */
> - DirtyStat.page_sampling.total_block_mem_MB += (info->ramblock_pages *
> - TARGET_PAGE_SIZE) >> 20;
> -}
> -
> -static void update_dirtyrate(uint64_t msec)
> -{
> - uint64_t dirtyrate;
> - uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples;
> - uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count;
> - uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB;
> -
> - dirtyrate = total_dirty_samples * total_block_mem_MB *
> - 1000 / (total_sample_count * msec);
> -
> - DirtyStat.dirty_rate = dirtyrate;
> + if (DirtyStat.page_sampling.readings) {
> + free(DirtyStat.page_sampling.readings);
> + DirtyStat.page_sampling.readings = NULL;
> + }
> }
>
> /*
> @@ -377,12 +385,14 @@ static bool save_ramblock_hash(struct RamblockDirtyInfo *info)
> return false;
> }
>
> - rand = g_rand_new();
> + rand = g_rand_new();
> + DirtyStat.page_sampling.n_total_pages += info->ramblock_pages;
> for (i = 0; i < sample_pages_count; i++) {
> info->sample_page_vfn[i] = g_rand_int_range(rand, 0,
> info->ramblock_pages - 1);
> info->hash_result[i] = get_ramblock_vfn_hash(info,
> info->sample_page_vfn[i]);
> + DirtyStat.page_sampling.n_sampled_pages++;
How about:
DirtyStat.page_sampling.n_sampled_pages += sample_pages_count;
out of the loop to avoid doing the calculation multiple times?
> }
> g_rand_free(rand);
>
> @@ -479,18 +489,20 @@ out:
> return ret;
> }
>
> -static void calc_page_dirty_rate(struct RamblockDirtyInfo *info)
> +static int64_t calc_page_dirty_rate(struct RamblockDirtyInfo *info)
> {
> uint32_t hash;
> int i;
>
> + int64_t n_dirty = 0;
> for (i = 0; i < info->sample_pages_count; i++) {
> hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]);
> if (hash != info->hash_result[i]) {
> + n_dirty++;
> trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]);
> - info->sample_dirty_count++;
> }
> }
> + return n_dirty;
> }
>
> static struct RamblockDirtyInfo *
> @@ -519,11 +531,12 @@ find_block_matched(RAMBlock *block, int count,
> return &infos[i];
> }
>
> -static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
> +static int64_t compare_page_hash_info(struct RamblockDirtyInfo *info,
> int block_count)
> {
> struct RamblockDirtyInfo *block_dinfo = NULL;
> RAMBlock *block = NULL;
> + int64_t n_dirty = 0;
>
> RAMBLOCK_FOREACH_MIGRATABLE(block) {
> if (skip_sample_ramblock(block)) {
> @@ -533,15 +546,10 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
> if (block_dinfo == NULL) {
> continue;
> }
> - calc_page_dirty_rate(block_dinfo);
> - update_dirtyrate_stat(block_dinfo);
> - }
> -
> - if (DirtyStat.page_sampling.total_sample_count == 0) {
> - return false;
> + n_dirty += calc_page_dirty_rate(block_dinfo);
> }
>
> - return true;
> + return n_dirty;
> }
>
> static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
> @@ -642,34 +650,77 @@ static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
> DirtyStat.dirty_rate = dirtyrate_sum;
> }
>
> +static int64_t increase_period(int64_t prev_period, int64_t max_period)
> +{
> + int64_t delta;
> + int64_t next_period;
> +
> + if (prev_period < 500) {
> + delta = 125;
> + } else if (prev_period < 1000) {
> + delta = 250;
> + } else if (prev_period < 2000) {
> + delta = 500;
> + } else if (prev_period < 4000) {
> + delta = 1000;
> + } else if (prev_period < 10000) {
> + delta = 2000;
> + } else {
> + delta = 5000;
> + }
> +
> + next_period = prev_period + delta;
> + if (next_period + delta >= max_period) {
> + next_period = max_period;
> + }
> + return next_period;
> +}
> +
> +
> static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
> {
> struct RamblockDirtyInfo *block_dinfo = NULL;
> int block_count = 0;
> - int64_t msec = 0;
> int64_t initial_time;
> + int64_t current_time;
>
> + /* initial pass */
> rcu_read_lock();
> initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
> - if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) {
> + bool ok = record_ramblock_hash_info(&block_dinfo, config, &block_count);
> + rcu_read_unlock();
> + if ((!ok) || (DirtyStat.page_sampling.n_sampled_pages == 0)) {
> goto out;
> }
> - rcu_read_unlock();
>
> - msec = config.sample_period_seconds * 1000;
> - msec = dirty_stat_wait(msec, initial_time);
> - DirtyStat.start_time = initial_time / 1000;
> - DirtyStat.calc_time = msec / 1000;
> + int64_t period = INITIAL_PERIOD_MS;
> + while (true) {
> + current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
> + int64_t delta = initial_time + period - current_time;
> + if (delta > 0) {
> + g_usleep(delta * 1000);
> + }
>
> - rcu_read_lock();
> - if (!compare_page_hash_info(block_dinfo, block_count)) {
> - goto out;
> - }
> + rcu_read_lock();
> + current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
> + int64_t n_dirty = compare_page_hash_info(block_dinfo, block_count);
> + rcu_read_unlock();
>
> - update_dirtyrate(msec);
> + SampleVMStat *ps = &DirtyStat.page_sampling;
> + ps->readings[ps->n_readings].period = current_time - initial_time;
> + ps->readings[ps->n_readings].n_dirty_pages = n_dirty;
> + ps->n_readings++;
> +
> + if (period >= DirtyStat.calc_time * 1000) {
> + int64_t mb_total = (ps->n_total_pages * TARGET_PAGE_SIZE) >> 20;
> + int64_t mb_dirty = n_dirty * mb_total / ps->n_sampled_pages;
> + DirtyStat.dirty_rate = mb_dirty * 1000 / period;
> + break;
> + }
> + period = increase_period(period, DirtyStat.calc_time * 1000);
> + }
>
> out:
> - rcu_read_unlock();
> free_ramblock_dirty_info(block_dinfo, block_count);
> }
>
> @@ -836,7 +887,23 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "(not ready)\n");
> }
>
> + if (info->has_n_total_pages) {
> + monitor_printf(mon, "Page count (page size %d):\n", TARGET_PAGE_SIZE);
> + monitor_printf(mon, " Total: %"PRIi64"\n", info->n_total_pages);
> + monitor_printf(mon, " Sampled: %"PRIi64"\n", info->n_sampled_pages);
> + int64List *periods = info->periods;
> + int64List *n_dirty_pages = info->n_dirty_pages;
> + while (periods) {
> + monitor_printf(mon, " Dirty(%"PRIi64"ms): %"PRIi64"\n",
> + periods->value, n_dirty_pages->value);
> + periods = periods->next;
> + n_dirty_pages = n_dirty_pages->next;
> + }
> + }
> +
> qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate);
> + qapi_free_int64List(info->periods);
> + qapi_free_int64List(info->n_dirty_pages);
> g_free(info);
> }
>
> diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h
> index 594a5c0bb6..7a97e2b076 100644
> --- a/migration/dirtyrate.h
> +++ b/migration/dirtyrate.h
> @@ -42,6 +42,18 @@
> #define MIN_SAMPLE_PAGE_COUNT 128
> #define MAX_SAMPLE_PAGE_COUNT 16384
>
> +/*
> + * Initial sampling period expressed in milliseconds
> + */
> +#define INITIAL_PERIOD_MS 125
> +
> +/*
> + * Upper bound on the number of DirtyReadings calculcated based on
Nit: calculcated -> calculated
> + * INITIAL_PERIOD_MS, MAX_FETCH_DIRTYRATE_TIME_SEC and increase_period()
> + */
> +#define MAX_DIRTY_READINGS 32
> +
> +
> struct DirtyRateConfig {
> uint64_t sample_pages_per_gigabytes; /* sample pages per GB */
> int64_t sample_period_seconds; /* time duration between two sampling */
> @@ -57,14 +69,19 @@ struct RamblockDirtyInfo {
> uint64_t ramblock_pages; /* ramblock size in TARGET_PAGE_SIZE */
> uint64_t *sample_page_vfn; /* relative offset address for sampled page */
> uint64_t sample_pages_count; /* count of sampled pages */
> - uint64_t sample_dirty_count; /* count of dirty pages we measure */
> uint32_t *hash_result; /* array of hash result for sampled pages */
> };
>
> +typedef struct DirtyReading {
> + int64_t period; /* time period in milliseconds */
> + int64_t n_dirty_pages; /* number of observed dirty pages */
> +} DirtyReading;
> +
> typedef struct SampleVMStat {
> - uint64_t total_dirty_samples; /* total dirty sampled page */
> - uint64_t total_sample_count; /* total sampled pages */
> - uint64_t total_block_mem_MB; /* size of total sampled pages in MB */
> + int64_t n_total_pages; /* total number of pages */
> + int64_t n_sampled_pages; /* number of sampled pages */
> + int64_t n_readings;
> + DirtyReading *readings;
> } SampleVMStat;
>
> /*
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 2c35b7b9cf..f818f51e0e 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -1805,6 +1805,22 @@
> # @vcpu-dirty-rate: dirtyrate for each vcpu if dirty-ring
> # mode specified (Since 6.2)
> #
> +# @page-size: page size in bytes (since 8.1)
> +#
> +# @n-total-pages: [page-sampling] total number of VM pages (since 8.1)
> +#
> +# @n-sampled-pages: [page-sampling] number of sampled VM pages (since 8.1)
> +#
> +# @periods: [page-sampling] array of time periods expressed in milliseconds
> +# for which dirty-sample measurements were collected (since 8.1)
> +#
> +# @n-dirty-pages: [page-sampling] number of pages among all sampled pages
> +# that were observed as changed during respective time period.
> +# i-th element of this array corresponds to the i-th element
> +# of the @periods array, i.e. @n-dirty-pages[i] is the number
> +# of dirtied pages during period of @periods[i] milliseconds
> +# after the initiation of calc-dirty-rate (since 8.1)
> +#
> # Since: 5.2
> ##
> { 'struct': 'DirtyRateInfo',
> @@ -1814,7 +1830,13 @@
> 'calc-time': 'int64',
> 'sample-pages': 'uint64',
> 'mode': 'DirtyRateMeasureMode',
> - '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } }
> + '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ],
> + 'page-size': 'int64',
> + '*n-total-pages': 'int64',
> + '*n-sampled-pages': 'int64',
> + '*periods': ['int64'],
> + '*n-dirty-pages': ['int64'] } }
> +
>
> ##
> # @calc-dirty-rate:
next prev parent reply other threads:[~2023-05-30 3:07 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-27 12:42 [PATCH v2 0/4] Migration time prediction using calc-dirty-rate Andrei Gudkov via
2023-04-27 12:42 ` [PATCH v2 1/4] migration/calc-dirty-rate: replaced CRC32 with xxHash Andrei Gudkov via
2023-05-10 16:54 ` Juan Quintela
2023-04-27 12:42 ` [PATCH v2 2/4] migration/calc-dirty-rate: detailed stats in sampling mode Andrei Gudkov via
2023-05-10 17:36 ` Juan Quintela
2023-05-12 13:18 ` gudkov.andrei--- via
2023-05-15 8:22 ` Juan Quintela
2023-05-18 14:45 ` gudkov.andrei--- via
2023-05-18 15:13 ` Juan Quintela
2023-05-15 8:23 ` Juan Quintela
2023-05-11 6:14 ` Markus Armbruster
2023-05-12 14:24 ` gudkov.andrei--- via
2023-05-30 3:06 ` Wang, Lei [this message]
2023-04-27 12:42 ` [PATCH v2 3/4] migration/calc-dirty-rate: added n-zero-pages metric Andrei Gudkov via
2023-05-10 17:57 ` Juan Quintela
2023-04-27 12:43 ` [PATCH v2 4/4] migration/calc-dirty-rate: tool to predict migration time Andrei Gudkov via
2023-05-10 18:01 ` Juan Quintela
2023-05-30 3:21 ` Wang, Lei
2023-06-02 13:06 ` gudkov.andrei--- via
2023-05-30 15:46 ` [PATCH v2 0/4] Migration time prediction using calc-dirty-rate Peter Xu
2023-05-31 14:46 ` gudkov.andrei--- via
2023-05-31 15:03 ` Peter Xu
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=40cc401c-a9c7-aca7-d80e-92ece193bb35@intel.com \
--to=lei4.wang@intel.com \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=eblake@redhat.com \
--cc=gudkov.andrei@huawei.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
--cc=zhengchuan@huawei.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).