diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index 3f913cc88a..81bce90c03 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -72,6 +72,11 @@ static void s390_tod_save(QEMUFile *f, void *opaque) qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); qemu_put_byte(f, tod.high); qemu_put_be64(f, tod.low); + + tdc->set(td, &tod, &err); + if (err) { + warn_report_err(err); + } } static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) diff --git a/stubs/async-run-on-cpu.c b/stubs/async-run-on-cpu.c new file mode 100644 index 0000000000..b8975bf5a8 --- /dev/null +++ b/stubs/async-run-on-cpu.c @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/cpu.h" + +void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) +{ + abort(); +} diff --git a/stubs/cpus-queue.c b/stubs/cpus-queue.c new file mode 100644 index 0000000000..c59fb76a10 --- /dev/null +++ b/stubs/cpus-queue.c @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/cpu.h" + +CPUTailQ cpus_queue = QTAILQ_HEAD_INITIALIZER(cpus_queue); diff --git a/stubs/meson.build b/stubs/meson.build index 0b2778c568..d3b551f4de 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -41,6 +41,8 @@ if have_block or have_ga stub_ss.add(files('monitor-internal.c')) stub_ss.add(files('qmp-command-available.c')) stub_ss.add(files('qmp-quit.c')) + stub_ss.add(files('async-run-on-cpu.c')) + stub_ss.add(files('cpus-queue.c')) endif if have_block or have_user diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 3bea6103ff..f714834a98 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -52,6 +52,10 @@ static int cpu_pre_save(void *opaque) kvm_s390_vcpu_interrupt_pre_save(cpu); } + if (tcg_enabled()) { + tcg_s390_tod_updated(CPU(cpu), RUN_ON_CPU_NULL); + } + return 0; } diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 2a6be4c7f9..d93a020064 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -30,6 +30,7 @@ #include "exec/icount.h" #include "system/replay.h" #include "system/cpus.h" +#include "hw/core/cpu.h" #ifdef CONFIG_POSIX #include @@ -387,11 +388,40 @@ static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) } } +static void timer_fire(CPUState *cpu, run_on_cpu_data data) +{ + QEMUTimer *t = data.host_ptr; + + t->cb(t->opaque); +} + static bool timer_mod_ns_locked(QEMUTimerList *timer_list, QEMUTimer *ts, int64_t expire_time) { QEMUTimer **pt, *t; + /* + * Normally during record-replay virtual clock timers and CPU work are + * deterministically ordered. This is because the virtual clock can be + * advanced only by instructions running on a CPU. + * + * A notable exception are timers that are armed already expired. Their + * expiration is not constrained by instruction execution, and, therefore, + * their ordering relative to CPU work is affected by what the + * record-replay thread is doing when they are armed. This introduces + * non-determinism. + * + * Convert such timers to CPU work in order to avoid it. + */ + if (replay_mode != REPLAY_MODE_NONE && + timer_list->clock->type == QEMU_CLOCK_VIRTUAL && + !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL) && + expire_time <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { + async_run_on_cpu(first_cpu, timer_fire, + RUN_ON_CPU_HOST_PTR(ts)); + return false; + } + /* add the timer in the sorted list */ pt = &timer_list->active_timers; for (;;) {