diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8a98446b7c74131dcaeb14bad67b30b8378c1c23..21eba582c03b34deeb0767de6bb63c96cf6989ff 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 af1df9aaebaaef382b4ac217785d4fab447f71d8..7fa923e2a5d2194b5b4b5b43047671f4d34aa8fa 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; @@ -217,9 +232,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 +808,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 +844,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 +985,11 @@ 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(); + 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 9ed883215225c55d2753090fd9b2c15bd5c7d7c5..020e0dd63936fc4b8689bedee0d92f97387008db 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -31,6 +31,27 @@ 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) +#define KVM_SET_RTC_CATCHUP_SPEED (1 << 3) + +struct kvm_rtc_reinject_control { + __u32 rtc_irq_coalesced; + __u8 flag; + __u8 speed; + __u8 reserved[30]; +}; + +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); 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 5d8e42b8f81d42339601e54c6eda71fff4d81623..e1155791a1e4dc109da4a9e1aaff8387ec4172e2 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/qapi/misc-target.json b/qapi/misc-target.json index 5aa2b95b7d4aa09a4b11a9bbabafa2b30ce4f2ae..c77f268c65660497cb8adb6e860ad71157520788 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 d35c897acb4fdc2b18070486b3a5cdc379e5e849..a760a46dc08b07c8b796eaedfa6c8ab9daf6159d 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 57bb8bba7c3a03c94f0a12ae6eab84ed4a985562..daac7c17cd56d22a0e3050ba53b4f4a02d148c5c 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -33,6 +33,9 @@ #include "qom/object.h" #include "sysemu/replay.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "qemu/log.h" +#include "qemu/config-file.h" static enum { RTC_BASE_UTC, @@ -200,3 +203,69 @@ 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); + } +} + +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 d8996f3d6eb647575803a59c076b38bdadfda90c..8fa8808cd0612e8df1e001a2142873bb27f79636 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 */ } },