target/mips/system/machine.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
The MIPS R4K CP0 timer (env->timer) is not included in vmstate_mips_cpu,
so after loadvm the QEMUTimer has no scheduled expiry. This causes
qemu_poll_ns() to block indefinitely and the guest to freeze until an
external I/O event (e.g. a keypress) wakes the main loop.
Fix by adding an optional vmstate subsection for the timer, following
the same pattern used by ARM (gt_timer), RISC-V (env.stimer), SPARC
(qtimer), and OpenRISC (timer).
The .needed callback returns false when env->timer is NULL (KVM mode),
keeping the subsection optional for backwards compatibility with
existing snapshots.
nit: when trying to reproduce of this bug requires savevm to work on
MIPS Malta. That is currently broken by a separate crash in
vmsd_can_compress() introduced by commit 7aa563630b ("pc: Start with
modern CPU hotplug interface by default"), which is being tracked and
fixed independently [1], [2]. This fix is independent of that crash and
touches no shared code.
[1] https://lore.kernel.org/qemu-devel/20260316194759.22672-1-farosas@suse.de/
[2] https://lore.kernel.org/qemu-devel/87341zsvmc.fsf@suse.de/
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1987
Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>
---
target/mips/system/machine.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/target/mips/system/machine.c b/target/mips/system/machine.c
index 8af11fd896..473d3ab036 100644
--- a/target/mips/system/machine.c
+++ b/target/mips/system/machine.c
@@ -3,6 +3,7 @@
#include "internal.h"
#include "migration/cpu.h"
#include "fpu_helper.h"
+#include "qemu/timer.h"
static int cpu_post_load(void *opaque, int version_id)
{
@@ -219,6 +220,23 @@ static const VMStateDescription vmstate_tlb = {
/* MIPS CPU state */
+static bool mips_timer_needed(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ return cpu->env.timer != NULL;
+}
+
+static const VMStateDescription mips_vmstate_timer = {
+ .name = "cpu/timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = mips_timer_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_TIMER_PTR(env.timer, MIPSCPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
.version_id = 21,
@@ -333,4 +351,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription * const []) {
+ &mips_vmstate_timer,
+ NULL
+ }
};
--
2.43.0
On 29/3/26 13:37, Trieu Huynh wrote:
> The MIPS R4K CP0 timer (env->timer) is not included in vmstate_mips_cpu,
> so after loadvm the QEMUTimer has no scheduled expiry. This causes
> qemu_poll_ns() to block indefinitely and the guest to freeze until an
> external I/O event (e.g. a keypress) wakes the main loop.
>
> Fix by adding an optional vmstate subsection for the timer, following
> the same pattern used by ARM (gt_timer), RISC-V (env.stimer), SPARC
> (qtimer), and OpenRISC (timer).
>
> The .needed callback returns false when env->timer is NULL (KVM mode),
> keeping the subsection optional for backwards compatibility with
> existing snapshots.
>
> nit: when trying to reproduce of this bug requires savevm to work on
> MIPS Malta. That is currently broken by a separate crash in
> vmsd_can_compress() introduced by commit 7aa563630b ("pc: Start with
> modern CPU hotplug interface by default"), which is being tracked and
> fixed independently [1], [2]. This fix is independent of that crash and
> touches no shared code.
>
> [1] https://lore.kernel.org/qemu-devel/20260316194759.22672-1-farosas@suse.de/
> [2] https://lore.kernel.org/qemu-devel/87341zsvmc.fsf@suse.de/
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1987
>
> Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>
> ---
> target/mips/system/machine.c | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
And queued via hw-misc tree (removing the nit), thanks.
On Sun, 29 Mar 2026 at 12:38, Trieu Huynh <vikingtc4@gmail.com> wrote:
>
> The MIPS R4K CP0 timer (env->timer) is not included in vmstate_mips_cpu,
> so after loadvm the QEMUTimer has no scheduled expiry. This causes
> qemu_poll_ns() to block indefinitely and the guest to freeze until an
> external I/O event (e.g. a keypress) wakes the main loop.
>
> Fix by adding an optional vmstate subsection for the timer, following
> the same pattern used by ARM (gt_timer), RISC-V (env.stimer), SPARC
> (qtimer), and OpenRISC (timer).
>
> The .needed callback returns false when env->timer is NULL (KVM mode),
> keeping the subsection optional for backwards compatibility with
> existing snapshots.
>
> nit: when trying to reproduce of this bug requires savevm to work on
> MIPS Malta. That is currently broken by a separate crash in
> vmsd_can_compress() introduced by commit 7aa563630b ("pc: Start with
> modern CPU hotplug interface by default"), which is being tracked and
> fixed independently [1], [2]. This fix is independent of that crash and
> touches no shared code.
This paragraph should IMHO go below the '---' line so it isn't in the
commit message proper.
> [1] https://lore.kernel.org/qemu-devel/20260316194759.22672-1-farosas@suse.de/
> [2] https://lore.kernel.org/qemu-devel/87341zsvmc.fsf@suse.de/
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1987
That bug's been hanging around a long time, and the writeup wasn't
exactly pointing a very strong finger at this as the problem --
nice work figuring out the cause.
> Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
For the current savevm-crash bug, I think this patch is probably
what we're going to take to fix it:
https://patchew.org/QEMU/20260330053008.2721532-1-zhao1.liu@intel.com/
thanks
-- PMM
© 2016 - 2026 Red Hat, Inc.