From c9e6144834c582738f205ddb6c045b0661768318 Mon Sep 17 00:00:00 2001 From: yang hang Date: Sat, 12 Feb 2022 15:13:14 +0800 Subject: [PATCH 1/2] hw/rtc: rtc irqs reinject in kvm The rtc is emulated in qemu but coalesced irqs are reinjected by kvm Signed-off-by: yang hang Signed-off-by: Bo Wan --- accel/kvm/kvm-all.c | 8 ++++++++ hw/rtc/mc146818rtc.c | 24 +++++++++++++++++++++++ include/qemu-common.h | 17 +++++++++++++++++ linux-headers/linux/kvm.h | 3 +++ softmmu/rtc.c | 40 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8a98446b7c..21eba582c0 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -45,6 +45,8 @@ #include "qemu/guest-random.h" #include "sysemu/hw_accel.h" #include "kvm-cpus.h" +#include "qemu/log.h" +#include "qemu-common.h" #include "hw/boards.h" @@ -170,6 +172,7 @@ bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; static bool kvm_immediate_exit; static hwaddr kvm_max_slot_size = ~0; +bool kvm_rtc_reinject_enable; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -2584,6 +2587,11 @@ static int kvm_init(MachineState *ms) kvm_irqchip_create(s); } + kvm_rtc_reinject_enable = (kvm_check_extension(kvm_state, KVM_CAP_RTC_IRQ_COALESCED) > 0); + if (!kvm_rtc_reinject_enable) { + qemu_log("kvm rtc irq reinjection not supported.\n"); + } + if (kvm_eventfds_allowed) { s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index af1df9aaeb..6e4d61ecbf 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -217,9 +217,17 @@ periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period, bo */ if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { uint32_t old_irq_coalesced = s->irq_coalesced; + if (kvm_rtc_reinject_enable) { + old_irq_coalesced += rtc_get_coalesced_irq(); + } lost_clock += old_irq_coalesced * old_period; s->irq_coalesced = lost_clock / s->period; + if (kvm_rtc_reinject_enable) { + rtc_set_coalesced_irq(s->irq_coalesced); + s->irq_coalesced = 0; + old_irq_coalesced = 0; + } lost_clock %= s->period; if (old_irq_coalesced != s->irq_coalesced || old_period != s->period) { @@ -785,6 +793,11 @@ static int rtc_pre_save(void *opaque) rtc_update_time(s); + if (kvm_rtc_reinject_enable && + s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + s->irq_coalesced += rtc_get_coalesced_irq(); + } + return 0; } @@ -816,6 +829,13 @@ static int rtc_post_load(void *opaque, int version_id) rtc_coalesced_timer_update(s); } } + + if (kvm_rtc_reinject_enable && s->irq_coalesced != 0 && + s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + rtc_set_coalesced_irq(s->irq_coalesced); + s->irq_coalesced = 0; + } + return 0; } @@ -950,6 +970,10 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date); qdev_init_gpio_out(dev, &s->irq, 1); + if (kvm_rtc_reinject_enable && + s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + rtc_lost_tick_policy_slew(); + } QLIST_INSERT_HEAD(&rtc_devices, s, link); } diff --git a/include/qemu-common.h b/include/qemu-common.h index 9ed8832152..a94c5723b1 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -31,6 +31,23 @@ time_t qemu_timedate_diff(struct tm *tm); time_t get_rtc_date_diff(void); void set_rtc_date_diff(time_t diff); +/* flags to control coalesced irq */ +#define KVM_GET_RTC_IRQ_COALESCED (1 << 0) +#define KVM_SET_RTC_IRQ_COALESCED (1 << 1) +#define KVM_RTC_LOST_TICK_POLICY_SLEW (1 << 2) + +struct kvm_rtc_reinject_control { + __u32 rtc_irq_coalesced; + __u8 flag; + __u8 reserved[31]; +}; + +extern bool kvm_rtc_reinject_enable; + +uint32_t rtc_get_coalesced_irq(void); +void rtc_set_coalesced_irq(uint32_t nr_irqs); +void rtc_lost_tick_policy_slew(void); + void *qemu_oom_check(void *ptr); ssize_t qemu_write_full(int fd, const void *buf, size_t count) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 5d8e42b8f8..e1155791a1 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1112,6 +1112,8 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_BINARY_STATS_FD 203 #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204 #define KVM_CAP_ARM_MTE 205 +/* RTC is emulated in qemu, but the colasced irqs are reinjected in kvm */ +#define KVM_CAP_RTC_IRQ_COALESCED 206 #define KVM_CAP_ARM_CPU_FEATURE 555 @@ -1385,6 +1387,7 @@ struct kvm_s390_ucas_mapping { #define KVM_S390_UCAS_UNMAP _IOW(KVMIO, 0x51, struct kvm_s390_ucas_mapping) #define KVM_S390_VCPU_FAULT _IOW(KVMIO, 0x52, unsigned long) +#define KVM_RTC_REINJECT_CONTROL _IOWR(KVMIO, 0x56, struct kvm_rtc_reinject_control) /* Device model IOC */ #define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60) #define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level) diff --git a/softmmu/rtc.c b/softmmu/rtc.c index 57bb8bba7c..0db9816451 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -33,6 +33,8 @@ #include "qom/object.h" #include "sysemu/replay.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "qemu/log.h" static enum { RTC_BASE_UTC, @@ -200,3 +202,41 @@ void configure_rtc(QemuOpts *opts) } } } + +uint32_t rtc_get_coalesced_irq(void) +{ + struct kvm_rtc_reinject_control control = {}; + int ret; + + control.flag = KVM_GET_RTC_IRQ_COALESCED; + ret = kvm_vm_ioctl(kvm_state, KVM_RTC_REINJECT_CONTROL, &control); + if (ret < 0) { + qemu_log("Failed to get coalesced irqs from kmod: %d\n", ret); + } + return control.rtc_irq_coalesced; +} + +void rtc_set_coalesced_irq(uint32_t nr_irqs) +{ + struct kvm_rtc_reinject_control control = {}; + int ret; + + control.rtc_irq_coalesced = nr_irqs; + control.flag = KVM_SET_RTC_IRQ_COALESCED; + ret = kvm_vm_ioctl(kvm_state, KVM_RTC_REINJECT_CONTROL, &control); + if (ret < 0) { + qemu_log("Failed to set coalesced irqs to kmod: %d, %u\n", ret, nr_irqs); + } +} + +void rtc_lost_tick_policy_slew(void) +{ + struct kvm_rtc_reinject_control control = {}; + int ret; + + control.flag = KVM_RTC_LOST_TICK_POLICY_SLEW; + ret = kvm_vm_ioctl(kvm_state, KVM_RTC_REINJECT_CONTROL, &control); + if (ret < 0) { + qemu_log("Failed to notify kvm to use lost tick policy slew: %d\n", ret); + } +} -- Gitee From 94e1a1e5c2b519e4da6eda7d13632a33a00c9bc9 Mon Sep 17 00:00:00 2001 From: yang hang Date: Sat, 12 Feb 2022 23:30:48 +0800 Subject: [PATCH 2/2] hw/rtc: set rtc catchup speed for per vm Supports setting the RTC clock compensation rate in kernel mode with virtual machine as granularity, instead of using the same speed for all virtual machines. A new configuration item catchup speed is added to libvirt to set the compensation rate of virtual machine QEMU Command: - RTC base = UTC, clock = VM, drivefix = slow, speed = 2 QEMU passes speed to kmod through IOCTL. The QMP command is provided to set the speed dynamically. Signed-off-by: yang hang Signed-off-by: Bo Wan --- hw/rtc/mc146818rtc.c | 16 ++++++++++++++++ include/qemu-common.h | 6 +++++- qapi/misc-target.json | 13 +++++++++++++ qapi/pragma.json | 3 ++- softmmu/rtc.c | 29 +++++++++++++++++++++++++++++ softmmu/vl.c | 3 +++ 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 6e4d61ecbf..7fa923e2a5 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -111,6 +111,21 @@ static QLIST_HEAD(, RTCState) rtc_devices = QLIST_HEAD_INITIALIZER(rtc_devices); #ifdef TARGET_I386 +void qmp_set_rtc_catchup_speed(const uint32_t speed, Error **errp) +{ + RTCState *s; + + if (!kvm_rtc_reinject_enable) { + return; + } + + QLIST_FOREACH(s, &rtc_devices, link) { + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + set_rtc_catchup_speed(speed); + } + } +} + void qmp_rtc_reset_reinjection(Error **errp) { RTCState *s; @@ -973,6 +988,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) if (kvm_rtc_reinject_enable && s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { rtc_lost_tick_policy_slew(); + set_rtc_catchup_speed(rtc_catchup_speed()); } QLIST_INSERT_HEAD(&rtc_devices, s, link); } diff --git a/include/qemu-common.h b/include/qemu-common.h index a94c5723b1..020e0dd639 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -35,11 +35,13 @@ void set_rtc_date_diff(time_t diff); #define KVM_GET_RTC_IRQ_COALESCED (1 << 0) #define KVM_SET_RTC_IRQ_COALESCED (1 << 1) #define KVM_RTC_LOST_TICK_POLICY_SLEW (1 << 2) +#define KVM_SET_RTC_CATCHUP_SPEED (1 << 3) struct kvm_rtc_reinject_control { __u32 rtc_irq_coalesced; __u8 flag; - __u8 reserved[31]; + __u8 speed; + __u8 reserved[30]; }; extern bool kvm_rtc_reinject_enable; @@ -47,6 +49,8 @@ extern bool kvm_rtc_reinject_enable; uint32_t rtc_get_coalesced_irq(void); void rtc_set_coalesced_irq(uint32_t nr_irqs); void rtc_lost_tick_policy_slew(void); +uint32_t rtc_catchup_speed(void); +void set_rtc_catchup_speed(const uint32_t speed); void *qemu_oom_check(void *ptr); diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 5aa2b95b7d..c77f268c65 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -55,6 +55,19 @@ 'if': 'TARGET_I386' } +## +# @set-rtc-catchup-speed: +# +# set rtc catchup speed +# +# @speed: rtc catchup speed +# +# Returns: Nothing on success +# +# Since: 6.2.0 +## +{ 'command': 'set-rtc-catchup-speed', 'data': {'speed': 'uint32'}, 'if': 'TARGET_I386'} + ## # @SevState: # diff --git a/qapi/pragma.json b/qapi/pragma.json index d35c897acb..a760a46dc0 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -29,7 +29,8 @@ 'ringbuf-read', 'query-rtc-date-diff', 'query-vhost-user-used-memslots', - 'query-vhost-kernel-used-memslots' ], + 'query-vhost-kernel-used-memslots', + 'set-rtc-catchup-speed' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status diff --git a/softmmu/rtc.c b/softmmu/rtc.c index 0db9816451..daac7c17cd 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -35,6 +35,7 @@ #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "qemu/log.h" +#include "qemu/config-file.h" static enum { RTC_BASE_UTC, @@ -240,3 +241,31 @@ void rtc_lost_tick_policy_slew(void) qemu_log("Failed to notify kvm to use lost tick policy slew: %d\n", ret); } } + +uint32_t rtc_catchup_speed(void) +{ + uint32_t speed; + QemuOpts *opts = qemu_find_opts_singleton("rtc"); + + speed = qemu_opt_get_number(opts, "speed", 0); + qemu_log("rtc catchup speed: %u\n", speed); + + return speed; +} + +void set_rtc_catchup_speed(const uint32_t speed) +{ + struct kvm_rtc_reinject_control control = {}; + int ret; + + if (speed > 0) { + control.flag = KVM_SET_RTC_CATCHUP_SPEED; + control.speed = speed; + ret = kvm_vm_ioctl(kvm_state, KVM_RTC_REINJECT_CONTROL, &control); + if (ret < 0) { + qemu_log("Failed to set rtc_catchup_speed: %d\n", ret); + return; + } + qemu_log("Success to set rtc_catchup_speed: %u\n", speed); + } +} diff --git a/softmmu/vl.c b/softmmu/vl.c index d8996f3d6e..8fa8808cd0 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -228,6 +228,9 @@ static QemuOptsList qemu_rtc_opts = { },{ .name = "driftfix", .type = QEMU_OPT_STRING, + },{ + .name = "speed", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, -- Gitee