From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647436729234949.3797959701349; Wed, 16 Mar 2022 06:18:49 -0700 (PDT) Received: from localhost ([::1]:59012 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTY7-0002DP-Sx for importer@patchew.org; Wed, 16 Mar 2022 09:18:48 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37492) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTNk-0004Eu-O3 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:08 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45232 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTNh-0001Tz-BX for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:03 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id F0DCB2800CD; Wed, 16 Mar 2022 21:07:53 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 9eb9f6b2cb184e15b0ed3eb511fdec7a for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:07:58 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 9eb9f6b2cb184e15b0ed3eb511fdec7a X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 1/9] accel/kvm/kvm-all: Refactor per-vcpu dirty ring reaping Date: Wed, 16 Mar 2022 21:07:13 +0800 Message-Id: X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647436730550100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Add a non-required argument 'CPUState' to kvm_dirty_ring_reap so that it can cover single vcpu dirty-ring-reaping scenario. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Reviewed-by: Peter Xu --- accel/kvm/kvm-all.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 27864df..29bf6a0 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -756,17 +756,20 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, = CPUState *cpu) } =20 /* Must be with slots_lock held */ -static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) +static uint64_t kvm_dirty_ring_reap_locked(KVMState *s, CPUState* cpu) { int ret; - CPUState *cpu; uint64_t total =3D 0; int64_t stamp; =20 stamp =3D get_clock(); =20 - CPU_FOREACH(cpu) { - total +=3D kvm_dirty_ring_reap_one(s, cpu); + if (cpu) { + total =3D kvm_dirty_ring_reap_one(s, cpu); + } else { + CPU_FOREACH(cpu) { + total +=3D kvm_dirty_ring_reap_one(s, cpu); + } } =20 if (total) { @@ -787,7 +790,7 @@ static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) * Currently for simplicity, we must hold BQL before calling this. We can * consider to drop the BQL if we're clear with all the race conditions. */ -static uint64_t kvm_dirty_ring_reap(KVMState *s) +static uint64_t kvm_dirty_ring_reap(KVMState *s, CPUState *cpu) { uint64_t total; =20 @@ -807,7 +810,7 @@ static uint64_t kvm_dirty_ring_reap(KVMState *s) * reset below. */ kvm_slots_lock(); - total =3D kvm_dirty_ring_reap_locked(s); + total =3D kvm_dirty_ring_reap_locked(s, cpu); kvm_slots_unlock(); =20 return total; @@ -854,7 +857,7 @@ static void kvm_dirty_ring_flush(void) * vcpus out in a synchronous way. */ kvm_cpu_synchronize_kick_all(); - kvm_dirty_ring_reap(kvm_state); + kvm_dirty_ring_reap(kvm_state, NULL); trace_kvm_dirty_ring_flush(1); } =20 @@ -1398,7 +1401,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, * Not easy. Let's cross the fingers until it's fixed. */ if (kvm_state->kvm_dirty_ring_size) { - kvm_dirty_ring_reap_locked(kvm_state); + kvm_dirty_ring_reap_locked(kvm_state, NULL); } else { kvm_slot_get_dirty_log(kvm_state, mem); } @@ -1470,7 +1473,7 @@ static void *kvm_dirty_ring_reaper_thread(void *data) r->reaper_state =3D KVM_DIRTY_RING_REAPER_REAPING; =20 qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(s); + kvm_dirty_ring_reap(s, NULL); qemu_mutex_unlock_iothread(); =20 r->reaper_iteration++; @@ -2957,7 +2960,7 @@ int kvm_cpu_exec(CPUState *cpu) */ trace_kvm_dirty_ring_full(cpu->cpu_index); qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(kvm_state); + kvm_dirty_ring_reap(kvm_state, NULL); qemu_mutex_unlock_iothread(); ret =3D 0; break; --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647436224507417.9001893059731; Wed, 16 Mar 2022 06:10:24 -0700 (PDT) Received: from localhost ([::1]:41652 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTPz-00077F-6m for importer@patchew.org; Wed, 16 Mar 2022 09:10:23 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37518) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTNp-0004FD-UN for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:10 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45245 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTNk-0001UF-H7 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:09 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id 5798B2800D7; Wed, 16 Mar 2022 21:07:59 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 7eef326b321c4c3cbb08c4de0216ee5e for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:02 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 7eef326b321c4c3cbb08c4de0216ee5e X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 2/9] cpus: Introduce cpu_list_generation_id Date: Wed, 16 Mar 2022 21:07:14 +0800 Message-Id: <88bf1196a2a7ef289542f33ba010f2cd5aee5f0c.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647436226754100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Introduce cpu_list_generation_id to track cpu list generation so that cpu hotplug/unplug can be detected during measurement of dirty page rate. cpu_list_generation_id could be used to detect changes of cpu list, which is prepared for dirty page rate measurement. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Reviewed-by: Peter Xu --- cpus-common.c | 8 ++++++++ include/exec/cpu-common.h | 1 + 2 files changed, 9 insertions(+) diff --git a/cpus-common.c b/cpus-common.c index 6e73d3e..31c6415 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -73,6 +73,12 @@ static int cpu_get_free_index(void) } =20 CPUTailQ cpus =3D QTAILQ_HEAD_INITIALIZER(cpus); +static unsigned int cpu_list_generation_id; + +unsigned int cpu_list_generation_id_get(void) +{ + return cpu_list_generation_id; +} =20 void cpu_list_add(CPUState *cpu) { @@ -84,6 +90,7 @@ void cpu_list_add(CPUState *cpu) assert(!cpu_index_auto_assigned); } QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); + cpu_list_generation_id++; } =20 void cpu_list_remove(CPUState *cpu) @@ -96,6 +103,7 @@ void cpu_list_remove(CPUState *cpu) =20 QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu->cpu_index =3D UNASSIGNED_CPU_INDEX; + cpu_list_generation_id++; } =20 CPUState *qemu_get_cpu(int index) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 7f7b594..856b0e7 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -32,6 +32,7 @@ extern intptr_t qemu_host_page_mask; void qemu_init_cpu_list(void); void cpu_list_lock(void); void cpu_list_unlock(void); +unsigned int cpu_list_generation_id_get(void); =20 void tcg_flush_softmmu_tlb(CPUState *cs); =20 --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647436878581661.4320514384583; Wed, 16 Mar 2022 06:21:18 -0700 (PDT) Received: from localhost ([::1]:38978 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTaX-0007rU-EU for importer@patchew.org; Wed, 16 Mar 2022 09:21:17 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37524) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTNr-0004Fl-0s for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:11 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45250 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTNn-0001UU-W5 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:10 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id A8E362800DA; Wed, 16 Mar 2022 21:08:02 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 83c3fee66e864b4d8fcfc58839411cce for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:05 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 83c3fee66e864b4d8fcfc58839411cce X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 3/9] migration/dirtyrate: Refactor dirty page rate calculation Date: Wed, 16 Mar 2022 21:07:15 +0800 Message-Id: <578d5f1ca746b38b11ca05b6b006cd945d5321ab.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647436880804100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) abstract out dirty log change logic into function global_dirty_log_change. abstract out dirty page rate calculation logic via dirty-ring into function vcpu_calculate_dirtyrate. abstract out mathematical dirty page rate calculation into do_calculate_dirtyrate, decouple it from DirtyStat. rename set_sample_page_period to dirty_stat_wait, which is well-understood and will be reused in dirtylimit. handle cpu hotplug/unplug scenario during measurement of dirty page rate. export util functions outside migration. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Reviewed-by: Peter Xu --- include/sysemu/dirtyrate.h | 28 ++++++ migration/dirtyrate.c | 227 ++++++++++++++++++++++++++++-------------= ---- migration/dirtyrate.h | 7 +- 3 files changed, 174 insertions(+), 88 deletions(-) create mode 100644 include/sysemu/dirtyrate.h diff --git a/include/sysemu/dirtyrate.h b/include/sysemu/dirtyrate.h new file mode 100644 index 0000000..4d3b9a4 --- /dev/null +++ b/include/sysemu/dirtyrate.h @@ -0,0 +1,28 @@ +/* + * dirty page rate helper functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(=E9=BB=84=E5=8B=87) + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_DIRTYRATE_H +#define QEMU_DIRTYRATE_H + +typedef struct VcpuStat { + int nvcpu; /* number of vcpu */ + DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ +} VcpuStat; + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot); + +void global_dirty_log_change(unsigned int flag, + bool start); +#endif diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index d65e744..79348de 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -46,7 +46,7 @@ static struct DirtyRateStat DirtyStat; static DirtyRateMeasureMode dirtyrate_mode =3D DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; =20 -static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) +static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time) { int64_t current_time; =20 @@ -60,6 +60,132 @@ static int64_t set_sample_page_period(int64_t msec, int= 64_t initial_time) return msec; } =20 +static inline void record_dirtypages(DirtyPageRecord *dirty_pages, + CPUState *cpu, bool start) +{ + if (start) { + dirty_pages[cpu->cpu_index].start_pages =3D cpu->dirty_pages; + } else { + dirty_pages[cpu->cpu_index].end_pages =3D cpu->dirty_pages; + } +} + +static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, + int64_t calc_time_ms) +{ + uint64_t memory_size_MB; + uint64_t increased_dirty_pages =3D + dirty_pages.end_pages - dirty_pages.start_pages; + + memory_size_MB =3D (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; + + return memory_size_MB * 1000 / calc_time_ms; +} + +void global_dirty_log_change(unsigned int flag, bool start) +{ + qemu_mutex_lock_iothread(); + if (start) { + memory_global_dirty_log_start(flag); + } else { + memory_global_dirty_log_stop(flag); + } + qemu_mutex_unlock_iothread(); +} + +/* + * global_dirty_log_sync + * 1. sync dirty log from kvm + * 2. stop dirty tracking if needed. + */ +static void global_dirty_log_sync(unsigned int flag, bool one_shot) +{ + qemu_mutex_lock_iothread(); + memory_global_dirty_log_sync(); + if (one_shot) { + memory_global_dirty_log_stop(flag); + } + qemu_mutex_unlock_iothread(); +} + +static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) +{ + CPUState *cpu; + DirtyPageRecord *records; + int nvcpu =3D 0; + + CPU_FOREACH(cpu) { + nvcpu++; + } + + stat->nvcpu =3D nvcpu; + stat->rates =3D g_malloc0(sizeof(DirtyRateVcpu) * nvcpu); + + records =3D g_malloc0(sizeof(DirtyPageRecord) * nvcpu); + + return records; +} + +static void vcpu_dirty_stat_collect(VcpuStat *stat, + DirtyPageRecord *records, + bool start) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + record_dirtypages(records, cpu, start); + } +} + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot) +{ + DirtyPageRecord *records; + int64_t init_time_ms; + int64_t duration; + int64_t dirtyrate; + int i =3D 0; + unsigned int gen_id; + +retry: + init_time_ms =3D qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + cpu_list_lock(); + gen_id =3D cpu_list_generation_id_get(); + records =3D vcpu_dirty_stat_alloc(stat); + vcpu_dirty_stat_collect(stat, records, true); + cpu_list_unlock(); + + duration =3D dirty_stat_wait(calc_time_ms, init_time_ms); + + global_dirty_log_sync(flag, one_shot); + + cpu_list_lock(); + if (gen_id !=3D cpu_list_generation_id_get()) { + g_free(records); + g_free(stat->rates); + cpu_list_unlock(); + goto retry; + } + vcpu_dirty_stat_collect(stat, records, false); + cpu_list_unlock(); + + for (i =3D 0; i < stat->nvcpu; i++) { + dirtyrate =3D do_calculate_dirtyrate(records[i], duration); + + stat->rates[i].id =3D i; + stat->rates[i].dirty_rate =3D dirtyrate; + + trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); + } + + g_free(records); + + return duration; +} + static bool is_sample_period_valid(int64_t sec) { if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC || @@ -396,44 +522,6 @@ static bool compare_page_hash_info(struct RamblockDirt= yInfo *info, return true; } =20 -static inline void record_dirtypages(DirtyPageRecord *dirty_pages, - CPUState *cpu, bool start) -{ - if (start) { - dirty_pages[cpu->cpu_index].start_pages =3D cpu->dirty_pages; - } else { - dirty_pages[cpu->cpu_index].end_pages =3D cpu->dirty_pages; - } -} - -static void dirtyrate_global_dirty_log_start(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static void dirtyrate_global_dirty_log_stop(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_sync(); - memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages) -{ - uint64_t memory_size_MB; - int64_t time_s; - uint64_t increased_dirty_pages =3D - dirty_pages.end_pages - dirty_pages.start_pages; - - memory_size_MB =3D (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; - time_s =3D DirtyStat.calc_time; - - return memory_size_MB / time_s; -} - static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, bool start) { @@ -444,11 +532,6 @@ static inline void record_dirtypages_bitmap(DirtyPageR= ecord *dirty_pages, } } =20 -static void do_calculate_dirtyrate_bitmap(DirtyPageRecord dirty_pages) -{ - DirtyStat.dirty_rate =3D do_calculate_dirtyrate_vcpu(dirty_pages); -} - static inline void dirtyrate_manual_reset_protect(void) { RAMBlock *block =3D NULL; @@ -492,71 +575,49 @@ static void calculate_dirtyrate_dirty_bitmap(struct D= irtyRateConfig config) DirtyStat.start_time =3D start_time / 1000; =20 msec =3D config.sample_period_seconds * 1000; - msec =3D set_sample_page_period(msec, start_time); + msec =3D dirty_stat_wait(msec, start_time); DirtyStat.calc_time =3D msec / 1000; =20 /* - * dirtyrate_global_dirty_log_stop do two things. + * do two things. * 1. fetch dirty bitmap from kvm * 2. stop dirty tracking */ - dirtyrate_global_dirty_log_stop(); + global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true); =20 record_dirtypages_bitmap(&dirty_pages, false); =20 - do_calculate_dirtyrate_bitmap(dirty_pages); + DirtyStat.dirty_rate =3D do_calculate_dirtyrate(dirty_pages, msec); } =20 static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) { - CPUState *cpu; - int64_t msec =3D 0; - int64_t start_time; + int64_t duration; uint64_t dirtyrate =3D 0; uint64_t dirtyrate_sum =3D 0; - DirtyPageRecord *dirty_pages; - int nvcpu =3D 0; int i =3D 0; =20 - CPU_FOREACH(cpu) { - nvcpu++; - } - - dirty_pages =3D malloc(sizeof(*dirty_pages) * nvcpu); - - DirtyStat.dirty_ring.nvcpu =3D nvcpu; - DirtyStat.dirty_ring.rates =3D malloc(sizeof(DirtyRateVcpu) * nvcpu); - - dirtyrate_global_dirty_log_start(); - - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, true); - } - - start_time =3D qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - DirtyStat.start_time =3D start_time / 1000; + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true); =20 - msec =3D config.sample_period_seconds * 1000; - msec =3D set_sample_page_period(msec, start_time); - DirtyStat.calc_time =3D msec / 1000; + DirtyStat.start_time =3D qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; =20 - dirtyrate_global_dirty_log_stop(); + /* calculate vcpu dirtyrate */ + duration =3D vcpu_calculate_dirtyrate(config.sample_period_seconds * 1= 000, + &DirtyStat.dirty_ring, + GLOBAL_DIRTY_DIRTY_RATE, + true); =20 - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, false); - } + DirtyStat.calc_time =3D duration / 1000; =20 + /* calculate vm dirtyrate */ for (i =3D 0; i < DirtyStat.dirty_ring.nvcpu; i++) { - dirtyrate =3D do_calculate_dirtyrate_vcpu(dirty_pages[i]); - trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); - - DirtyStat.dirty_ring.rates[i].id =3D i; + dirtyrate =3D DirtyStat.dirty_ring.rates[i].dirty_rate; DirtyStat.dirty_ring.rates[i].dirty_rate =3D dirtyrate; dirtyrate_sum +=3D dirtyrate; } =20 DirtyStat.dirty_rate =3D dirtyrate_sum; - free(dirty_pages); } =20 static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) @@ -574,7 +635,7 @@ static void calculate_dirtyrate_sample_vm(struct DirtyR= ateConfig config) rcu_read_unlock(); =20 msec =3D config.sample_period_seconds * 1000; - msec =3D set_sample_page_period(msec, initial_time); + msec =3D dirty_stat_wait(msec, initial_time); DirtyStat.start_time =3D initial_time / 1000; DirtyStat.calc_time =3D msec / 1000; =20 diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h index 69d4c5b..594a5c0 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -13,6 +13,8 @@ #ifndef QEMU_MIGRATION_DIRTYRATE_H #define QEMU_MIGRATION_DIRTYRATE_H =20 +#include "sysemu/dirtyrate.h" + /* * Sample 512 pages per GB as default. */ @@ -65,11 +67,6 @@ typedef struct SampleVMStat { uint64_t total_block_mem_MB; /* size of total sampled pages in MB */ } SampleVMStat; =20 -typedef struct VcpuStat { - int nvcpu; /* number of vcpu */ - DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ -} VcpuStat; - /* * Store calculation statistics for each measure. */ --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647437155940468.5626945858912; Wed, 16 Mar 2022 06:25:55 -0700 (PDT) Received: from localhost ([::1]:47542 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTf0-0005OJ-Hs for importer@patchew.org; Wed, 16 Mar 2022 09:25:54 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37560) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTNt-0004HA-UX for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:14 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45257 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTNq-0001Ua-Pq for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:13 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id 0F1F82800DC; Wed, 16 Mar 2022 21:08:06 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id e16285e778734b19bc6d0401a2737592 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:09 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: e16285e778734b19bc6d0401a2737592 X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 4/9] softmmu/dirtylimit: Implement vCPU dirtyrate calculation periodically Date: Wed, 16 Mar 2022 21:07:16 +0800 Message-Id: <789f4a79177a86ecfbb0ddeec7a967dedc1c2a55.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647437157787100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Introduce the third method GLOBAL_DIRTY_LIMIT of dirty tracking for calculate dirtyrate periodly for dirty page rate limit. Add dirtylimit.c to implement dirtyrate calculation periodly, which will be used for dirty page rate limit. Add dirtylimit.h to export util functions for dirty page rate limit implementation. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Reviewed-by: Peter Xu --- include/exec/memory.h | 5 +- include/sysemu/dirtylimit.h | 22 +++++++++ softmmu/dirtylimit.c | 116 ++++++++++++++++++++++++++++++++++++++++= ++++ softmmu/meson.build | 1 + 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 include/sysemu/dirtylimit.h create mode 100644 softmmu/dirtylimit.c diff --git a/include/exec/memory.h b/include/exec/memory.h index 4d5997e..88ca510 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -69,7 +69,10 @@ static inline void fuzz_dma_read_cb(size_t addr, /* Dirty tracking enabled because measuring dirty rate */ #define GLOBAL_DIRTY_DIRTY_RATE (1U << 1) =20 -#define GLOBAL_DIRTY_MASK (0x3) +/* Dirty tracking enabled because dirty limit */ +#define GLOBAL_DIRTY_LIMIT (1U << 2) + +#define GLOBAL_DIRTY_MASK (0x7) =20 extern unsigned int global_dirty_tracking; =20 diff --git a/include/sysemu/dirtylimit.h b/include/sysemu/dirtylimit.h new file mode 100644 index 0000000..da459f0 --- /dev/null +++ b/include/sysemu/dirtylimit.h @@ -0,0 +1,22 @@ +/* + * Dirty page rate limit common functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(=E9=BB=84=E5=8B=87) + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_DIRTYRLIMIT_H +#define QEMU_DIRTYRLIMIT_H + +#define DIRTYLIMIT_CALC_TIME_MS 1000 /* 1000ms */ + +int64_t vcpu_dirty_rate_get(int cpu_index); +void vcpu_dirty_rate_stat_start(void); +void vcpu_dirty_rate_stat_stop(void); +void vcpu_dirty_rate_stat_initialize(void); +void vcpu_dirty_rate_stat_finalize(void); +#endif diff --git a/softmmu/dirtylimit.c b/softmmu/dirtylimit.c new file mode 100644 index 0000000..6102e8c --- /dev/null +++ b/softmmu/dirtylimit.c @@ -0,0 +1,116 @@ +/* + * Dirty page rate limit implementation code + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(=E9=BB=84=E5=8B=87) + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qapi/qapi-commands-migration.h" +#include "sysemu/dirtyrate.h" +#include "sysemu/dirtylimit.h" +#include "exec/memory.h" +#include "hw/boards.h" + +struct { + VcpuStat stat; + bool running; + QemuThread thread; +} *vcpu_dirty_rate_stat; + +static void vcpu_dirty_rate_stat_collect(void) +{ + VcpuStat stat; + int i =3D 0; + + /* calculate vcpu dirtyrate */ + vcpu_calculate_dirtyrate(DIRTYLIMIT_CALC_TIME_MS, + &stat, + GLOBAL_DIRTY_LIMIT, + false); + + for (i =3D 0; i < stat.nvcpu; i++) { + vcpu_dirty_rate_stat->stat.rates[i].id =3D i; + vcpu_dirty_rate_stat->stat.rates[i].dirty_rate =3D + stat.rates[i].dirty_rate; + } + + free(stat.rates); +} + +static void *vcpu_dirty_rate_stat_thread(void *opaque) +{ + rcu_register_thread(); + + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, true); + + while (qatomic_read(&vcpu_dirty_rate_stat->running)) { + vcpu_dirty_rate_stat_collect(); + } + + /* stop log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, false); + + rcu_unregister_thread(); + return NULL; +} + +int64_t vcpu_dirty_rate_get(int cpu_index) +{ + DirtyRateVcpu *rates =3D vcpu_dirty_rate_stat->stat.rates; + return qatomic_read(&rates[cpu_index].dirty_rate); +} + +void vcpu_dirty_rate_stat_start(void) +{ + if (qatomic_read(&vcpu_dirty_rate_stat->running)) { + return; + } + + qatomic_set(&vcpu_dirty_rate_stat->running, 1); + qemu_thread_create(&vcpu_dirty_rate_stat->thread, + "dirtyrate-stat", + vcpu_dirty_rate_stat_thread, + NULL, + QEMU_THREAD_JOINABLE); +} + +void vcpu_dirty_rate_stat_stop(void) +{ + qatomic_set(&vcpu_dirty_rate_stat->running, 0); + qemu_mutex_unlock_iothread(); + qemu_thread_join(&vcpu_dirty_rate_stat->thread); + qemu_mutex_lock_iothread(); +} + +void vcpu_dirty_rate_stat_initialize(void) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + int max_cpus =3D ms->smp.max_cpus; + + vcpu_dirty_rate_stat =3D + g_malloc0(sizeof(*vcpu_dirty_rate_stat)); + + vcpu_dirty_rate_stat->stat.nvcpu =3D max_cpus; + vcpu_dirty_rate_stat->stat.rates =3D + g_malloc0(sizeof(DirtyRateVcpu) * max_cpus); + + vcpu_dirty_rate_stat->running =3D false; +} + +void vcpu_dirty_rate_stat_finalize(void) +{ + free(vcpu_dirty_rate_stat->stat.rates); + vcpu_dirty_rate_stat->stat.rates =3D NULL; + + free(vcpu_dirty_rate_stat); + vcpu_dirty_rate_stat =3D NULL; +} diff --git a/softmmu/meson.build b/softmmu/meson.build index 8138248..3272af1 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -4,6 +4,7 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( 'memory.c', 'physmem.c', 'qtest.c', + 'dirtylimit.c', )]) =20 specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647437175055351.90874835148657; Wed, 16 Mar 2022 06:26:15 -0700 (PDT) Received: from localhost ([::1]:49444 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTfK-0006qy-37 for importer@patchew.org; Wed, 16 Mar 2022 09:26:14 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37598) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTO0-0004X9-Ek for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:20 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45273 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTNy-0001W3-N7 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:20 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id 74A3B2800DD; Wed, 16 Mar 2022 21:08:09 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id df00bc8261fc4c74b74fba2536e06b17 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:17 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: df00bc8261fc4c74b74fba2536e06b17 X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 5/9] accel/kvm/kvm-all: Introduce kvm_dirty_ring_size function Date: Wed, 16 Mar 2022 21:07:17 +0800 Message-Id: <823c35ac86f0197df54e00d1a749a91519eb3096.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647437176192100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Introduce kvm_dirty_ring_size util function to help calculate dirty ring ful time. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Acked-by: Peter Xu --- accel/kvm/kvm-all.c | 5 +++++ accel/stubs/kvm-stub.c | 6 ++++++ include/sysemu/kvm.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 29bf6a0..0c78bc7 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2313,6 +2313,11 @@ bool kvm_dirty_ring_enabled(void) return kvm_state->kvm_dirty_ring_size ? true : false; } =20 +uint32_t kvm_dirty_ring_size(void) +{ + return kvm_state->kvm_dirty_ring_size; +} + static int kvm_init(MachineState *ms) { MachineClass *mc =3D MACHINE_GET_CLASS(ms); diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 3345882..c5aafaa 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -148,3 +148,9 @@ bool kvm_dirty_ring_enabled(void) { return false; } + +uint32_t kvm_dirty_ring_size(void) +{ + return 0; +} +#endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a783c78..efd6dee 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -582,4 +582,6 @@ bool kvm_cpu_check_are_resettable(void); bool kvm_arch_cpu_check_are_resettable(void); =20 bool kvm_dirty_ring_enabled(void); + +uint32_t kvm_dirty_ring_size(void); #endif --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647437342904956.0695554344537; Wed, 16 Mar 2022 06:29:02 -0700 (PDT) Received: from localhost ([::1]:57766 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTi1-0004CE-T4 for importer@patchew.org; Wed, 16 Mar 2022 09:29:01 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37624) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTO5-0004qD-Gk for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:25 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45277 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTO2-0001Wn-RP for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:25 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id 977522800E5; Wed, 16 Mar 2022 21:08:17 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 4beb96d390e14d77bb72c4327ccbe13a for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:21 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 4beb96d390e14d77bb72c4327ccbe13a X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 6/9] softmmu/dirtylimit: Implement virtual CPU throttle Date: Wed, 16 Mar 2022 21:07:18 +0800 Message-Id: <795b60905c1e89543f37a94a3ce58ca1d5eae7d0.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647437344012100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Setup a negative feedback system when vCPU thread handling KVM_EXIT_DIRTY_RING_FULL exit by introducing throttle_us_per_full field in struct CPUState. Sleep throttle_us_per_full microseconds to throttle vCPU if dirtylimit is in service. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Reviewed-by: Peter Xu --- accel/kvm/kvm-all.c | 20 ++- include/hw/core/cpu.h | 6 + include/sysemu/dirtylimit.h | 15 +++ softmmu/dirtylimit.c | 291 ++++++++++++++++++++++++++++++++++++++++= ++++ softmmu/trace-events | 7 ++ 5 files changed, 338 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 0c78bc7..2e6f319 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -45,6 +45,7 @@ #include "qemu/guest-random.h" #include "sysemu/hw_accel.h" #include "kvm-cpus.h" +#include "sysemu/dirtylimit.h" =20 #include "hw/boards.h" =20 @@ -476,6 +477,7 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) cpu->kvm_state =3D s; cpu->vcpu_dirty =3D true; cpu->dirty_pages =3D 0; + cpu->throttle_us_per_full =3D 0; =20 mmap_size =3D kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -1469,6 +1471,11 @@ static void *kvm_dirty_ring_reaper_thread(void *data) */ sleep(1); =20 + /* keep sleeping so that dirtylimit not be interfered by reaper */ + if (dirtylimit_in_service()) { + continue; + } + trace_kvm_dirty_ring_reaper("wakeup"); r->reaper_state =3D KVM_DIRTY_RING_REAPER_REAPING; =20 @@ -2965,8 +2972,19 @@ int kvm_cpu_exec(CPUState *cpu) */ trace_kvm_dirty_ring_full(cpu->cpu_index); qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(kvm_state, NULL); + /* + * We throttle vCPU by making it sleep once it exit from kernel + * due to dirty ring full. In the dirtylimit scenario, reaping + * all vCPUs after a single vCPU dirty ring get full result in + * the miss of sleep, so just reap the ring-fulled vCPU. + */ + if (dirtylimit_in_service()) { + kvm_dirty_ring_reap(kvm_state, cpu); + } else { + kvm_dirty_ring_reap(kvm_state, NULL); + } qemu_mutex_unlock_iothread(); + dirtylimit_vcpu_execute(cpu); ret =3D 0; break; case KVM_EXIT_SYSTEM_EVENT: diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 0efc615..a3f6b2d 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -418,6 +418,12 @@ struct CPUState { */ bool throttle_thread_scheduled; =20 + /* + * Sleep throttle_us_per_full microseconds once dirty ring is full + * if dirty page rate limit is enabled. + */ + int64_t throttle_us_per_full; + bool ignore_memory_transaction_failures; =20 /* Used for user-only emulation of prctl(PR_SET_UNALIGN). */ diff --git a/include/sysemu/dirtylimit.h b/include/sysemu/dirtylimit.h index da459f0..8d2c1f3 100644 --- a/include/sysemu/dirtylimit.h +++ b/include/sysemu/dirtylimit.h @@ -19,4 +19,19 @@ void vcpu_dirty_rate_stat_start(void); void vcpu_dirty_rate_stat_stop(void); void vcpu_dirty_rate_stat_initialize(void); void vcpu_dirty_rate_stat_finalize(void); + +void dirtylimit_state_lock(void); +void dirtylimit_state_unlock(void); +void dirtylimit_state_initialize(void); +void dirtylimit_state_finalize(void); +bool dirtylimit_in_service(void); +bool dirtylimit_vcpu_index_valid(int cpu_index); +void dirtylimit_process(void); +void dirtylimit_change(bool start); +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable); +void dirtylimit_set_all(uint64_t quota, + bool enable); +void dirtylimit_vcpu_execute(CPUState *cpu); #endif diff --git a/softmmu/dirtylimit.c b/softmmu/dirtylimit.c index 6102e8c..76d0b44 100644 --- a/softmmu/dirtylimit.c +++ b/softmmu/dirtylimit.c @@ -18,6 +18,26 @@ #include "sysemu/dirtylimit.h" #include "exec/memory.h" #include "hw/boards.h" +#include "sysemu/kvm.h" +#include "trace.h" + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ +/* + * Plus or minus vcpu sleep time linearly if dirty + * page rate error value percentage over + * DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT. + * Otherwise, plus or minus a fixed vcpu sleep time. + */ +#define DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT 50 +/* + * Max vcpu sleep time percentage during a cycle + * composed of dirty ring full and sleep time. + */ +#define DIRTYLIMIT_THROTTLE_PCT_MAX 99 =20 struct { VcpuStat stat; @@ -25,6 +45,30 @@ struct { QemuThread thread; } *vcpu_dirty_rate_stat; =20 +typedef struct VcpuDirtyLimitState { + int cpu_index; + bool enabled; + /* + * Quota dirty page rate, unit is MB/s + * zero if not enabled. + */ + uint64_t quota; +} VcpuDirtyLimitState; + +struct { + VcpuDirtyLimitState *states; + /* Max cpus number configured by user */ + int max_cpus; + /* Number of vcpu under dirtylimit */ + int limited_nvcpu; +} *dirtylimit_state; + +/* protect dirtylimit_state */ +static QemuMutex dirtylimit_mutex; + +/* dirtylimit thread quit if dirtylimit_quit is true */ +static bool dirtylimit_quit; + static void vcpu_dirty_rate_stat_collect(void) { VcpuStat stat; @@ -54,6 +98,9 @@ static void *vcpu_dirty_rate_stat_thread(void *opaque) =20 while (qatomic_read(&vcpu_dirty_rate_stat->running)) { vcpu_dirty_rate_stat_collect(); + if (dirtylimit_in_service()) { + dirtylimit_process(); + } } =20 /* stop log sync */ @@ -86,9 +133,11 @@ void vcpu_dirty_rate_stat_start(void) void vcpu_dirty_rate_stat_stop(void) { qatomic_set(&vcpu_dirty_rate_stat->running, 0); + dirtylimit_state_unlock(); qemu_mutex_unlock_iothread(); qemu_thread_join(&vcpu_dirty_rate_stat->thread); qemu_mutex_lock_iothread(); + dirtylimit_state_lock(); } =20 void vcpu_dirty_rate_stat_initialize(void) @@ -114,3 +163,245 @@ void vcpu_dirty_rate_stat_finalize(void) free(vcpu_dirty_rate_stat); vcpu_dirty_rate_stat =3D NULL; } + +void dirtylimit_state_lock(void) +{ + qemu_mutex_lock(&dirtylimit_mutex); +} + +void dirtylimit_state_unlock(void) +{ + qemu_mutex_unlock(&dirtylimit_mutex); +} + +static void +__attribute__((__constructor__)) dirtylimit_mutex_init(void) +{ + qemu_mutex_init(&dirtylimit_mutex); +} + +static inline VcpuDirtyLimitState *dirtylimit_vcpu_get_state(int cpu_index) +{ + return &dirtylimit_state->states[cpu_index]; +} + +void dirtylimit_state_initialize(void) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + int max_cpus =3D ms->smp.max_cpus; + int i; + + dirtylimit_state =3D g_malloc0(sizeof(*dirtylimit_state)); + + dirtylimit_state->states =3D + g_malloc0(sizeof(VcpuDirtyLimitState) * max_cpus); + + for (i =3D 0; i < max_cpus; i++) { + dirtylimit_state->states[i].cpu_index =3D i; + } + + dirtylimit_state->max_cpus =3D max_cpus; + trace_dirtylimit_state_initialize(max_cpus); +} + +void dirtylimit_state_finalize(void) +{ + free(dirtylimit_state->states); + dirtylimit_state->states =3D NULL; + + free(dirtylimit_state); + dirtylimit_state =3D NULL; + + trace_dirtylimit_state_finalize(); +} + +bool dirtylimit_in_service(void) +{ + return !!dirtylimit_state; +} + +bool dirtylimit_vcpu_index_valid(int cpu_index) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + + return !(cpu_index < 0 || + cpu_index >=3D ms->smp.max_cpus); +} + +static inline int64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate) +{ + static uint64_t max_dirtyrate; + uint32_t dirty_ring_size =3D kvm_dirty_ring_size(); + uint64_t dirty_ring_size_meory_MB =3D + dirty_ring_size * TARGET_PAGE_SIZE >> 20; + + if (max_dirtyrate < dirtyrate) { + max_dirtyrate =3D dirtyrate; + } + + return dirty_ring_size_meory_MB * 1000000 / max_dirtyrate; +} + +static inline bool dirtylimit_done(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min =3D MIN(quota, current); + max =3D MAX(quota, current); + + return ((max - min) <=3D DIRTYLIMIT_TOLERANCE_RANGE) ? true : false; +} + +static inline bool +dirtylimit_need_linear_adjustment(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min =3D MIN(quota, current); + max =3D MAX(quota, current); + + return ((max - min) * 100 / max) > DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT; +} + +static void dirtylimit_set_throttle(CPUState *cpu, + uint64_t quota, + uint64_t current) +{ + int64_t ring_full_time_us =3D 0; + uint64_t sleep_pct =3D 0; + uint64_t throttle_us =3D 0; + + if (current =3D=3D 0) { + cpu->throttle_us_per_full =3D 0; + return; + } + + ring_full_time_us =3D dirtylimit_dirty_ring_full_time(current); + + if (dirtylimit_need_linear_adjustment(quota, current)) { + if (quota < current) { + sleep_pct =3D (current - quota) * 100 / current; + throttle_us =3D + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full +=3D throttle_us; + } else { + sleep_pct =3D (quota - current) * 100 / quota; + throttle_us =3D + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full -=3D throttle_us; + } + + trace_dirtylimit_throttle_pct(cpu->cpu_index, + sleep_pct, + throttle_us); + } else { + if (quota < current) { + cpu->throttle_us_per_full +=3D ring_full_time_us / 10; + } else { + cpu->throttle_us_per_full -=3D ring_full_time_us / 10; + } + } + + /* + * TODO: in the big kvm_dirty_ring_size case (eg: 65536, or other scen= ario), + * current dirty page rate may never reach the quota, we should = stop + * increasing sleep time? + */ + cpu->throttle_us_per_full =3D MIN(cpu->throttle_us_per_full, + ring_full_time_us * DIRTYLIMIT_THROTTLE_PCT_MAX); + + cpu->throttle_us_per_full =3D MAX(cpu->throttle_us_per_full, 0); +} + +static void dirtylimit_adjust_throttle(CPUState *cpu) +{ + uint64_t quota =3D 0; + uint64_t current =3D 0; + int cpu_index =3D cpu->cpu_index; + + quota =3D dirtylimit_vcpu_get_state(cpu_index)->quota; + current =3D vcpu_dirty_rate_get(cpu_index); + + if (!dirtylimit_done(quota, current)) { + dirtylimit_set_throttle(cpu, quota, current); + } + + return; +} + +void dirtylimit_process(void) +{ + CPUState *cpu; + + if (!qatomic_read(&dirtylimit_quit)) { + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return; + } + + CPU_FOREACH(cpu) { + if (!dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) { + continue; + } + dirtylimit_adjust_throttle(cpu); + } + dirtylimit_state_unlock(); + } +} + +void dirtylimit_change(bool start) +{ + if (start) { + qatomic_set(&dirtylimit_quit, 0); + } else { + qatomic_set(&dirtylimit_quit, 1); + } +} + +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable) +{ + trace_dirtylimit_set_vcpu(cpu_index, quota); + + if (enable) { + dirtylimit_state->states[cpu_index].quota =3D quota; + if (!dirtylimit_vcpu_get_state(cpu_index)->enabled) { + dirtylimit_state->limited_nvcpu++; + } + } else { + dirtylimit_state->states[cpu_index].quota =3D 0; + if (dirtylimit_state->states[cpu_index].enabled) { + dirtylimit_state->limited_nvcpu--; + } + } + + dirtylimit_state->states[cpu_index].enabled =3D enable; +} + +void dirtylimit_set_all(uint64_t quota, + bool enable) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + int max_cpus =3D ms->smp.max_cpus; + int i; + + for (i =3D 0; i < max_cpus; i++) { + dirtylimit_set_vcpu(i, quota, enable); + } +} + +void dirtylimit_vcpu_execute(CPUState *cpu) +{ + if (dirtylimit_in_service() && + dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled && + cpu->throttle_us_per_full) { + trace_dirtylimit_vcpu_execute(cpu->cpu_index, + cpu->throttle_us_per_full); + usleep(cpu->throttle_us_per_full); + } +} diff --git a/softmmu/trace-events b/softmmu/trace-events index 9c88887..22606dc 100644 --- a/softmmu/trace-events +++ b/softmmu/trace-events @@ -31,3 +31,10 @@ runstate_set(int current_state, const char *current_stat= e_str, int new_state, co system_wakeup_request(int reason) "reason=3D%d" qemu_system_shutdown_request(int reason) "reason=3D%d" qemu_system_powerdown_request(void) "" + +#dirtylimit.c +dirtylimit_state_initialize(int max_cpus) "dirtylimit state initialize: ma= x cpus %d" +dirtylimit_state_finalize(void) +dirtylimit_throttle_pct(int cpu_index, uint64_t pct, int64_t time_us) "CPU= [%d] throttle percent: %" PRIu64 ", throttle adjust time %"PRIi64 " us" +dirtylimit_set_vcpu(int cpu_index, uint64_t quota) "CPU[%d] set dirty page= rate limit %"PRIu64 +dirtylimit_vcpu_execute(int cpu_index, int64_t sleep_time_us) "CPU[%d] sle= ep %"PRIi64 " us" --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647436560740691.3364516123197; Wed, 16 Mar 2022 06:16:00 -0700 (PDT) Received: from localhost ([::1]:53202 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTVP-0006du-Gw for importer@patchew.org; Wed, 16 Mar 2022 09:15:59 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37680) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTO9-00052o-4C for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:29 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45283 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTO6-0001Xm-83 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:28 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id ED9A82800E8; Wed, 16 Mar 2022 21:08:21 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 17b272e8030b4b09b81a59be9dc7089f for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:24 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 17b272e8030b4b09b81a59be9dc7089f X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 7/9] softmmu/dirtylimit: Implement dirty page rate limit Date: Wed, 16 Mar 2022 21:07:19 +0800 Message-Id: X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647436561687100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Implement dirtyrate calculation periodically basing on dirty-ring and throttle virtual CPU until it reachs the quota dirty page rate given by user. Introduce qmp commands "set-vcpu-dirty-limit", "cancel-vcpu-dirty-limit", "query-vcpu-dirty-limit" to enable, disable, query dirty page limit for virtual CPU. Meanwhile, introduce corresponding hmp commands "set_vcpu_dirty_limit", "cancel_vcpu_dirty_limit", "info vcpu_dirty_limit" so the feature can be more usable. "query-vcpu-dirty-limit" success depends on enabling dirty page rate limit, so just add it to the list of skipped command to ensure qmp-cmd-test run successfully. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) Acked-by: Markus Armbruster Reviewed-by: Peter Xu --- hmp-commands-info.hx | 13 +++ hmp-commands.hx | 32 ++++++++ include/monitor/hmp.h | 3 + qapi/migration.json | 80 +++++++++++++++++++ softmmu/dirtylimit.c | 195 +++++++++++++++++++++++++++++++++++++++++= ++++ tests/qtest/qmp-cmd-test.c | 2 + 6 files changed, 325 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index adfa085..016717d 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -865,6 +865,19 @@ SRST Display the vcpu dirty rate information. ERST =20 + { + .name =3D "vcpu_dirty_limit", + .args_type =3D "", + .params =3D "", + .help =3D "show dirty page limit information of all vCPU", + .cmd =3D hmp_info_vcpu_dirty_limit, + }, + +SRST + ``info vcpu_dirty_limit`` + Display the vcpu dirty page limit information. +ERST + #if defined(TARGET_I386) { .name =3D "sgx", diff --git a/hmp-commands.hx b/hmp-commands.hx index 8476277..82ab75f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1746,3 +1746,35 @@ ERST "\n\t\t\t -b to specify dirty bitmap as method of ca= lculation)", .cmd =3D hmp_calc_dirty_rate, }, + +SRST +``set_vcpu_dirty_limit`` + Set dirty page rate limit on virtual CPU, the information about all the + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_li= mit`` + command. +ERST + + { + .name =3D "set_vcpu_dirty_limit", + .args_type =3D "dirty_rate:l,cpu_index:l?", + .params =3D "dirty_rate [cpu_index]", + .help =3D "set dirty page rate limit, use cpu_index to set l= imit" + "\n\t\t\t\t\t on a specified virtual cpu", + .cmd =3D hmp_set_vcpu_dirty_limit, + }, + +SRST +``cancel_vcpu_dirty_limit`` + Cancel dirty page rate limit on virtual CPU, the information about all t= he + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_li= mit`` + command. +ERST + + { + .name =3D "cancel_vcpu_dirty_limit", + .args_type =3D "cpu_index:l?", + .params =3D "[cpu_index]", + .help =3D "cancel dirty page rate limit, use cpu_index to ca= ncel" + "\n\t\t\t\t\t limit on a specified virtual cpu", + .cmd =3D hmp_cancel_vcpu_dirty_limit, + }, diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 96d0148..478820e 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -131,6 +131,9 @@ void hmp_replay_delete_break(Monitor *mon, const QDict = *qdict); void hmp_replay_seek(Monitor *mon, const QDict *qdict); void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict); +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); void hmp_human_readable_text_helper(Monitor *mon, HumanReadableText *(*qmp_handler)(Erro= r **)); =20 diff --git a/qapi/migration.json b/qapi/migration.json index 18e2610..910a4ff 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1861,6 +1861,86 @@ { 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } =20 ## +# @DirtyLimitInfo: +# +# Dirty page rate limit information of a virtual CPU. +# +# @cpu-index: index of a virtual CPU. +# +# @limit-rate: upper limit of dirty page rate (MB/s) for a virtual +# CPU, 0 means unlimited. +# +# @current-rate: current dirty page rate (MB/s) for a virtual CPU. +# +# Since: 7.0 +# +## +{ 'struct': 'DirtyLimitInfo', + 'data': { 'cpu-index': 'int', + 'limit-rate': 'uint64', + 'current-rate': 'uint64' } } + +## +# @set-vcpu-dirty-limit: +# +# Set the upper limit of dirty page rate for virtual CPUs. +# +# Requires KVM with accelerator property "dirty-ring-size" set. +# A virtual CPU's dirty page rate is a measure of its memory load. +# To observe dirty page rates, use @calc-dirty-rate. +# +# @cpu-index: index of a virtual CPU, default is all. +# +# @dirty-rate: upper limit of dirty page rate (MB/s) for virtual CPUs. +# +# Since: 7.0 +# +# Example: +# {"execute": "set-vcpu-dirty-limit"} +# "arguments": { "dirty-rate": 200, +# "cpu-index": 1 } } +# +## +{ 'command': 'set-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int', + 'dirty-rate': 'uint64' } } + +## +# @cancel-vcpu-dirty-limit: +# +# Cancel the upper limit of dirty page rate for virtual CPUs. +# +# Cancel the dirty page limit for the vCPU which has been set with +# set-vcpu-dirty-limit command. Note that this command requires +# support from dirty ring, same as the "set-vcpu-dirty-limit". +# +# @cpu-index: index of a virtual CPU, default is all. +# +# Since: 7.0 +# +# Example: +# {"execute": "cancel-vcpu-dirty-limit"} +# "arguments": { "cpu-index": 1 } } +# +## +{ 'command': 'cancel-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int'} } + +## +# @query-vcpu-dirty-limit: +# +# Returns information about virtual CPU dirty page rate limits, if any. +# +# Since: 7.0 +# +# Example: +# {"execute": "query-vcpu-dirty-limit"} +# +## +{ 'command': 'query-vcpu-dirty-limit', + 'returns': [ 'DirtyLimitInfo' ] } + +## # @snapshot-save: # # Save a VM snapshot diff --git a/softmmu/dirtylimit.c b/softmmu/dirtylimit.c index 76d0b44..365bd43 100644 --- a/softmmu/dirtylimit.c +++ b/softmmu/dirtylimit.c @@ -14,8 +14,12 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" #include "sysemu/dirtyrate.h" #include "sysemu/dirtylimit.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" #include "exec/memory.h" #include "hw/boards.h" #include "sysemu/kvm.h" @@ -405,3 +409,194 @@ void dirtylimit_vcpu_execute(CPUState *cpu) usleep(cpu->throttle_us_per_full); } } + +static void dirtylimit_init(void) +{ + dirtylimit_state_initialize(); + dirtylimit_change(true); + vcpu_dirty_rate_stat_initialize(); + vcpu_dirty_rate_stat_start(); +} + +static void dirtylimit_cleanup(void) +{ + vcpu_dirty_rate_stat_stop(); + vcpu_dirty_rate_stat_finalize(); + dirtylimit_change(false); + dirtylimit_state_finalize(); +} + +void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirtylimit_in_service()) { + return; + } + + dirtylimit_state_lock(); + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, 0, false); + } else { + dirtylimit_set_all(0, false); + } + + if (!dirtylimit_state->limited_nvcpu) { + dirtylimit_cleanup(); + } + + dirtylimit_state_unlock(); +} + +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t cpu_index =3D qdict_get_try_int(qdict, "cpu_index", -1); + Error *err =3D NULL; + + qmp_cancel_vcpu_dirty_limit(!!(cpu_index !=3D -1), cpu_index, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +void qmp_set_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + uint64_t dirty_rate, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + error_setg(errp, "dirty page limit feature requires KVM with" + " accelerator property 'dirty-ring-size' set'"); + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirty_rate) { + qmp_cancel_vcpu_dirty_limit(has_cpu_index, cpu_index, errp); + return; + } + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_init(); + } + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, dirty_rate, true); + } else { + dirtylimit_set_all(dirty_rate, true); + } + + dirtylimit_state_unlock(); +} + +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t dirty_rate =3D qdict_get_int(qdict, "dirty_rate"); + int64_t cpu_index =3D qdict_get_try_int(qdict, "cpu_index", -1); + Error *err =3D NULL; + + qmp_set_vcpu_dirty_limit(!!(cpu_index !=3D -1), cpu_index, dirty_rate,= &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +static struct DirtyLimitInfo *dirtylimit_query_vcpu(int cpu_index) +{ + DirtyLimitInfo *info =3D NULL; + + info =3D g_malloc0(sizeof(*info)); + info->cpu_index =3D cpu_index; + info->limit_rate =3D dirtylimit_vcpu_get_state(cpu_index)->quota; + info->current_rate =3D vcpu_dirty_rate_get(cpu_index); + + return info; +} + +static struct DirtyLimitInfoList *dirtylimit_query_all(void) +{ + int i, index; + DirtyLimitInfo *info =3D NULL; + DirtyLimitInfoList *head =3D NULL, **tail =3D &head; + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return NULL; + } + + for (i =3D 0; i < dirtylimit_state->max_cpus; i++) { + index =3D dirtylimit_state->states[i].cpu_index; + if (dirtylimit_vcpu_get_state(index)->enabled) { + info =3D dirtylimit_query_vcpu(index); + QAPI_LIST_APPEND(tail, info); + } + } + + dirtylimit_state_unlock(); + + return head; +} + +struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) +{ + if (!dirtylimit_in_service()) { + error_setg(errp, "dirty page limit not enabled"); + return NULL; + } + + return dirtylimit_query_all(); +} + +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + DirtyLimitInfoList *limit, *head, *info =3D NULL; + Error *err =3D NULL; + + if (!dirtylimit_in_service()) { + monitor_printf(mon, "Dirty page limit not enabled!\n"); + return; + } + + info =3D qmp_query_vcpu_dirty_limit(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + head =3D info; + for (limit =3D head; limit !=3D NULL; limit =3D limit->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)= ," + " current rate %"PRIi64 " (MB/s)\n", + limit->value->cpu_index, + limit->value->limit_rate, + limit->value->current_rate); + } + + g_free(info); +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 7f103ea..4b216a0 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -110,6 +110,8 @@ static bool query_is_ignored(const char *cmd) "query-sev-capabilities", "query-sgx", "query-sgx-capabilities", + /* Success depends on enabling dirty page rate limit */ + "query-vcpu-dirty-limit", NULL }; int i; --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1647437294725885.7822338364986; Wed, 16 Mar 2022 06:28:14 -0700 (PDT) Received: from localhost ([::1]:56028 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUThF-0002zx-AG for importer@patchew.org; Wed, 16 Mar 2022 09:28:13 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37708) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTOD-0005H5-EX for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:33 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45290 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTOA-0001Yp-DY for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:33 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id 293282800EC; Wed, 16 Mar 2022 21:08:25 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id 2e9296348a294551bae1440ab4b30c99 for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:29 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: 2e9296348a294551bae1440ab4b30c99 X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 8/9] migration-test: Export migration-test util funtions Date: Wed, 16 Mar 2022 21:07:20 +0800 Message-Id: X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647437296052100001 From: Hyman Huang(=E9=BB=84=E5=8B=87) Dirtylimit qtest can reuse the mechanisms that have been implemented by migration-test to start a vm, so export the relevant util functions. Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) --- tests/qtest/migration-helpers.c | 87 ++++++++++++++++++++++++++ tests/qtest/migration-helpers.h | 12 ++++ tests/qtest/migration-test.c | 132 ++++++------------------------------= ---- 3 files changed, 118 insertions(+), 113 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helper= s.c index 4ee2601..4ae4bf1 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -188,3 +188,90 @@ void wait_for_migration_fail(QTestState *from, bool al= low_active) g_assert(qdict_get_bool(rsp_return, "running")); qobject_unref(rsp_return); } + +void init_bootfile(const char *bootpath, void *content, size_t len) +{ + FILE *bootfile =3D fopen(bootpath, "wb"); + + g_assert_cmpint(fwrite(content, len, 1, bootfile), =3D=3D, 1); + fclose(bootfile); +} + +/* + * Wait for some output in the serial output file, + * we get an 'A' followed by an endless string of 'B's + * but on the destination we won't have the A. + */ +void wait_for_serial(const char *tmpfs, const char *side) +{ + g_autofree char *serialpath =3D g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile =3D fopen(serialpath, "r"); + const char *arch =3D qtest_get_arch(); + int started =3D (strcmp(side, "src_serial") =3D=3D 0 && + strcmp(arch, "ppc64") =3D=3D 0) ? 0 : 1; + + do { + int readvalue =3D fgetc(serialfile); + + if (!started) { + /* SLOF prints its banner before starting test, + * to ignore it, mark the start of the test with '_', + * ignore all characters until this marker + */ + switch (readvalue) { + case '_': + started =3D 1; + break; + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + } + continue; + } + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + started =3D (strcmp(side, "src_serial") =3D=3D 0 && + strcmp(arch, "ppc64") =3D=3D 0) ? 0 : 1; + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, sid= e); + g_assert_not_reached(); + } + } while (true); +} + +bool kvm_dirty_ring_supported(void) +{ +#if defined(__linux__) && defined(HOST_X86_64) + int ret, kvm_fd =3D open("/dev/kvm", O_RDONLY); + + if (kvm_fd < 0) { + return false; + } + + ret =3D ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); + close(kvm_fd); + + /* We test with 4096 slots */ + if (ret < 4096) { + return false; + } + + return true; +#else + return false; +#endif +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helper= s.h index d63bba9..a34cd2f 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -14,6 +14,12 @@ =20 #include "libqos/libqtest.h" =20 +/* For dirty ring test; so far only x86_64 is supported */ +#if defined(__linux__) && defined(HOST_X86_64) +#include "linux/kvm.h" +#endif +#include + extern bool got_stop; =20 GCC_FMT_ATTR(3, 4) @@ -34,4 +40,10 @@ void wait_for_migration_complete(QTestState *who); =20 void wait_for_migration_fail(QTestState *from, bool allow_active); =20 +void init_bootfile(const char *bootpath, void *content, size_t len); + +void wait_for_serial(const char *tmpfs, const char *side); + +bool kvm_dirty_ring_supported(void); + #endif /* MIGRATION_HELPERS_H_ */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0870656..fe5e0a9 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -27,11 +27,6 @@ #include "migration-helpers.h" #include "tests/migration/migration-test.h" =20 -/* For dirty ring test; so far only x86_64 is supported */ -#if defined(__linux__) && defined(HOST_X86_64) -#include "linux/kvm.h" -#endif - /* TODO actually test the results and get rid of this */ #define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS_= _)) =20 @@ -49,7 +44,6 @@ static bool uffd_feature_thread_id; =20 #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVEN= TFD) #include -#include #include =20 static bool ufd_version_check(void) @@ -100,70 +94,6 @@ static const char *tmpfs; #include "tests/migration/aarch64/a-b-kernel.h" #include "tests/migration/s390x/a-b-bios.h" =20 -static void init_bootfile(const char *bootpath, void *content, size_t len) -{ - FILE *bootfile =3D fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), =3D=3D, 1); - fclose(bootfile); -} - -/* - * Wait for some output in the serial output file, - * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A. - */ -static void wait_for_serial(const char *side) -{ - g_autofree char *serialpath =3D g_strdup_printf("%s/%s", tmpfs, side); - FILE *serialfile =3D fopen(serialpath, "r"); - const char *arch =3D qtest_get_arch(); - int started =3D (strcmp(side, "src_serial") =3D=3D 0 && - strcmp(arch, "ppc64") =3D=3D 0) ? 0 : 1; - - do { - int readvalue =3D fgetc(serialfile); - - if (!started) { - /* SLOF prints its banner before starting test, - * to ignore it, mark the start of the test with '_', - * ignore all characters until this marker - */ - switch (readvalue) { - case '_': - started =3D 1; - break; - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - } - continue; - } - switch (readvalue) { - case 'A': - /* Fine */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - started =3D (strcmp(side, "src_serial") =3D=3D 0 && - strcmp(arch, "ppc64") =3D=3D 0) ? 0 : 1; - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, sid= e); - g_assert_not_reached(); - } - } while (true); -} - /* * It's tricky to use qemu's migration event capability with qtest, * events suddenly appearing confuse the qmp()/hmp() responses. @@ -279,7 +209,6 @@ static void check_guests_ram(QTestState *who) static void cleanup(const char *filename) { g_autofree char *path =3D g_strdup_printf("%s/%s", tmpfs, filename); - unlink(path); } =20 @@ -684,7 +613,7 @@ static int migrate_postcopy_prepare(QTestState **from_p= tr, migrate_set_parameter_int(from, "downtime-limit", 1); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -701,7 +630,7 @@ static void migrate_postcopy_complete(QTestState *from,= QTestState *to) wait_for_migration_complete(from); =20 /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); =20 if (uffd_feature_thread_id) { read_blocktime(to); @@ -821,7 +750,7 @@ static void test_precopy_unix_common(bool dirty_ring) migrate_set_parameter_int(from, "max-bandwidth", 1000000000); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -835,7 +764,7 @@ static void test_precopy_unix_common(bool dirty_ring) =20 qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); =20 test_migrate_end(from, to, true); @@ -868,7 +797,7 @@ static void test_ignore_shared(void) migrate_set_capability(to, "x-ignore-shared", true); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -880,7 +809,7 @@ static void test_ignore_shared(void) =20 qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); =20 /* Check whether shared RAM has been really skipped */ @@ -914,7 +843,7 @@ static void test_xbzrle(const char *uri) migrate_set_capability(from, "xbzrle", true); migrate_set_capability(to, "xbzrle", true); /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -930,7 +859,7 @@ static void test_xbzrle(const char *uri) } qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); =20 test_migrate_end(from, to, true); @@ -964,7 +893,7 @@ static void test_precopy_tcp(void) migrate_set_parameter_int(from, "max-bandwidth", 1000000000); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 uri =3D migrate_get_socket_address(to, "socket-address"); =20 @@ -979,7 +908,7 @@ static void test_precopy_tcp(void) } qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); =20 test_migrate_end(from, to, true); @@ -1009,7 +938,7 @@ static void test_migrate_fd_proto(void) migrate_set_parameter_int(from, "max-bandwidth", 1000000000); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 /* Create two connected sockets for migration */ ret =3D socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); @@ -1064,7 +993,7 @@ static void test_migrate_fd_proto(void) qobject_unref(rsp); =20 /* Complete migration */ - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); test_migrate_end(from, to, true); } @@ -1087,7 +1016,7 @@ static void do_test_validate_uuid(MigrateStart *args,= bool should_fail) migrate_set_capability(from, "validate-uuid", true); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -1186,7 +1115,7 @@ static void test_migrate_auto_converge(void) migrate_set_capability(from, "pause-before-switchover", true); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 migrate_qmp(from, uri, "{}"); =20 @@ -1221,7 +1150,7 @@ static void test_migrate_auto_converge(void) =20 qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); =20 =20 @@ -1264,7 +1193,7 @@ static void test_multifd_tcp(const char *method) qobject_unref(rsp); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 uri =3D migrate_get_socket_address(to, "socket-address"); =20 @@ -1279,7 +1208,7 @@ static void test_multifd_tcp(const char *method) } qtest_qmp_eventwait(to, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); test_migrate_end(from, to, true); } @@ -1347,7 +1276,7 @@ static void test_multifd_tcp_cancel(void) qobject_unref(rsp); =20 /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); + wait_for_serial(tmpfs, "src_serial"); =20 uri =3D migrate_get_socket_address(to, "socket-address"); =20 @@ -1392,34 +1321,11 @@ static void test_multifd_tcp_cancel(void) } qtest_qmp_eventwait(to2, "RESUME"); =20 - wait_for_serial("dest_serial"); + wait_for_serial(tmpfs, "dest_serial"); wait_for_migration_complete(from); test_migrate_end(from, to2, true); } =20 -static bool kvm_dirty_ring_supported(void) -{ -#if defined(__linux__) && defined(HOST_X86_64) - int ret, kvm_fd =3D open("/dev/kvm", O_RDONLY); - - if (kvm_fd < 0) { - return false; - } - - ret =3D ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); - close(kvm_fd); - - /* We test with 4096 slots */ - if (ret < 4096) { - return false; - } - - return true; -#else - return false; -#endif -} - int main(int argc, char **argv) { char template[] =3D "/tmp/migration-test-XXXXXX"; --=20 1.8.3.1 From nobody Sat May 18 13:16:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 164743678612830.530631019490784; Wed, 16 Mar 2022 06:19:46 -0700 (PDT) Received: from localhost ([::1]:33210 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTZ3-0003sG-2V for importer@patchew.org; Wed, 16 Mar 2022 09:19:45 -0400 Received: from eggs.gnu.org ([209.51.188.92]:37728) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUTOG-0005PH-BQ for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:36 -0400 Received: from prt-mail.chinatelecom.cn ([42.123.76.228]:45298 helo=chinatelecom.cn) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUTOD-0001ZJ-UX for qemu-devel@nongnu.org; Wed, 16 Mar 2022 09:08:36 -0400 Received: from clientip-182.150.57.243 (unknown [172.18.0.188]) by chinatelecom.cn (HERMES) with SMTP id A0D6B2800F1; Wed, 16 Mar 2022 21:08:29 +0800 (CST) Received: from ([172.18.0.188]) by app0023 with ESMTP id faca8d7b745148c28f3f5a665f4a226c for qemu-devel@nongnu.org; Wed, 16 Mar 2022 21:08:32 CST HMM_SOURCE_IP: 172.18.0.188:41560.1721706285 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP X-189-SAVE-TO-SEND: +huangy81@chinatelecom.cn X-Transaction-ID: faca8d7b745148c28f3f5a665f4a226c X-Real-From: huangy81@chinatelecom.cn X-Receive-IP: 172.18.0.188 X-MEDUSA-Status: 0 From: huangy81@chinatelecom.cn To: qemu-devel Subject: [PATCH v21 9/9] tests: Add dirty page rate limit test Date: Wed, 16 Mar 2022 21:07:21 +0800 Message-Id: <22a74578fb2127fc65fd98b0c04ed3a7706a7f08.1647435820.git.huangy81@chinatelecom.cn> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: References: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=42.123.76.228; envelope-from=huangy81@chinatelecom.cn; helo=chinatelecom.cn X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , Juan Quintela , Hyman , David Hildenbrand , Richard Henderson , Markus Armbruster , Peter Xu , "Dr. David Alan Gilbert" , Paolo Bonzini , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZM-MESSAGEID: 1647436788174100005 From: Hyman Huang(=E9=BB=84=E5=8B=87) Add dirty page rate limit test if kernel support dirty ring, create a standalone file to implement the test case. The following qmp commands are covered by this test case: "calc-dirty-rate", "query-dirty-rate", "set-vcpu-dirty-limit", "cancel-vcpu-dirty-limit" and "query-vcpu-dirty-limit". Signed-off-by: Hyman Huang(=E9=BB=84=E5=8B=87) --- tests/qtest/dirtylimit-test.c | 327 ++++++++++++++++++++++++++++++++++++++= ++++ tests/qtest/meson.build | 2 + 2 files changed, 329 insertions(+) create mode 100644 tests/qtest/dirtylimit-test.c diff --git a/tests/qtest/dirtylimit-test.c b/tests/qtest/dirtylimit-test.c new file mode 100644 index 0000000..b8d9960 --- /dev/null +++ b/tests/qtest/dirtylimit-test.c @@ -0,0 +1,327 @@ +/* + * QTest testcase for Dirty Page Rate Limit + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(=E9=BB=84=E5=8B=87) + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqos/libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" + +#include "migration-helpers.h" +#include "tests/migration/i386/a-b-bootblock.h" + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +static const char *tmpfs; + +static QDict *qmp_command(QTestState *who, const char *command, ...) +{ + va_list ap; + QDict *resp, *ret; + + va_start(ap, command); + resp =3D qtest_vqmp(who, command, ap); + va_end(ap); + + g_assert(!qdict_haskey(resp, "error")); + g_assert(qdict_haskey(resp, "return")); + + ret =3D qdict_get_qdict(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + + return ret; +} + +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qobject_unref(qmp_command(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %ld," + "'mode': 'dirty-ring' }}", + calc_time)); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qmp_command(who, "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qobject_unref(qmp_command(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %ld } }", + dirtyrate)); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qobject_unref(qmp_command(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }")); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp =3D qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + gchar *status; + + rsp_return =3D query_dirty_rate(who); + g_assert(rsp_return); + + status =3D g_strdup(qdict_get_str(rsp_return, "status")); + g_assert(status); + + return g_strcmp0(status, "measuring"); +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t calc_time) +{ + int max_try_count =3D 200; + usleep(calc_time); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 200 ms(max_try_count * 1000us), + * if dirtyrate measurement not complete, test failed. + */ + g_assert_cmpint(max_try_count, !=3D, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + gchar *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return =3D query_dirty_rate(who); + g_assert(rsp_return); + + status =3D g_strdup(qdict_get_str(rsp_return, "status")); + g_assert(status); + g_assert_cmpstr(status, =3D=3D, "measured"); + + rates =3D qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry =3D qlist_first(rates); + g_assert(entry); + + rate =3D qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate =3D qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return =3D query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates =3D qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry =3D qlist_first(rates); + g_assert(entry); + + rate =3D qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate =3D qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *start_vm(void) +{ + QTestState *vm =3D NULL; + g_autofree gchar *cmd =3D NULL; + const char *arch =3D qtest_get_arch(); + g_autofree char *bootpath =3D NULL; + + assert((strcmp(arch, "x86_64") =3D=3D 0)); + bootpath =3D g_strdup_printf("%s/bootsect", tmpfs); + assert(sizeof(x86_bootsect) =3D=3D 512); + init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); + + cmd =3D g_strdup_printf("-accel kvm,dirty-ring-size=3D4096 " + "-name dirtylimit-test,debug-threads=3Don " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=3D%s,format=3Draw ", + tmpfs, bootpath); + + vm =3D qtest_init(cmd); + return vm; +} + +static void cleanup(const char *filename) +{ + g_autofree char *path =3D g_strdup_printf("%s/%s", tmpfs, filename); + unlink(path); +} + +static void stop_vm(QTestState *vm) +{ + qtest_quit(vm); + cleanup("bootsect"); + cleanup("vm_serial"); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count =3D 5; + int hit =3D 0; + + vm =3D start_vm(); + if (!vm) { + return; + } + + /* Wait for the first serial output from the vm*/ + wait_for_serial(tmpfs, "vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep a calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1 * 1000000); + + /* Query original dirty page rate */ + origin_rate =3D get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory */ + assert(origin_rate !=3D 0); + + /* Setup quota dirty page rate at one-third of origin */ + quota_rate =3D origin_rate / 3; + + /* Set dirtylimit and wait a bit to check if it take effect */ + dirtylimit_set_all(vm, quota_rate); + usleep(2000000); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, =3D=3D, get_limit_rate(vm)); + + /* Check if dirtylimit take effect realistically */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1 * 1000000); + rate =3D get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit =3D 1; + break; + } + } + + g_assert_cmpint(hit, =3D=3D, 1); + + hit =3D 0; + max_try_count =3D 5; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1 * 1000000); + rate =3D get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit =3D 1; + break; + } + } + + g_assert_cmpint(hit, =3D=3D, 1); + stop_vm(vm); +} + +int main(int argc, char **argv) +{ + char template[] =3D "/tmp/dirtylimit-test-XXXXXX"; + int ret; + + tmpfs =3D mkdtemp(template); + if (!tmpfs) { + g_test_message("mkdtemp on path (%s): %s", template, strerror(errn= o)); + } + g_assert(tmpfs); + + if (!kvm_dirty_ring_supported()) { + return 0; + } + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/dirtylimit/test", test_vcpu_dirty_limit); + ret =3D g_test_run(); + + g_assert_cmpint(ret, =3D=3D, 0); + + ret =3D rmdir(tmpfs); + if (ret !=3D 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index d25f82b..6b041e0 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -32,6 +32,7 @@ qtests_generic =3D \ 'qom-test', 'test-hmp', 'qos-test', + 'dirtylimit-test', ] if config_host.has_key('CONFIG_MODULES') qtests_generic +=3D [ 'modules-test' ] @@ -296,6 +297,7 @@ qtests =3D { 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), + 'dirtylimit-test': files('migration-helpers.c'), } =20 if dbus_display --=20 1.8.3.1