Skip to contentSkip to conversation

fix QEMU hang after hibernate

PATCH In review John Coiner 2 participants 2 messages Oct 10, 2005 – Nov 15, 2005

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 fix QEMU hang after hibernate

After I hibernate my x86 linux host machine (using the "swsusp2" package) and resume, QEMU and guest hang. That is odd, because hibernation is transparent to all other user programs.

Has anyone else noticed this? If so please try the patch.

You know QEMU is doing something weird, for it to be sensitive to hibernation. Indeed, the cause is something you don't see every day:

QEMU implements timers partly by reading the CPU's tick counter directly from hardware, with an architecture-specific assembly instruction ('rdtsc' on intel, and so on for other archs) rather than using purely OS-provided features to get the time.

The problem is that following hibernation, the CPU tick counter isn't what it used to be. On my host, it has a lower value -- it jumped backwards. This breaks QEMU's timers. Some of them decide to block for a very long time, which causes the guest to hang or behave oddly.

(Maybe this is technically a bug in "swsusp2", maybe it should restore the CPU tick counter to the previous value upon resume. But who cares. It's easy to fix this in QEMU, and it would not be easy to check and fix all hibernation schemes on all architectures and operating systems.)

The following patch fixes the 'cpu_get_ticks()' function to always return a non-decreasing value, even if the value read from hardware decreases. Hope it helps. Feedback is welcome.

-- John

vl.c 2005-10-10 00:54:08.000000000 -0400 renamed from vl.c 2005-09-04 13:11:31.000000000 -0400 +9 -2
@@ -545,14 +547,21 @@
 #error unsupported CPU
 #endif
 
-static int64_t cpu_ticks_offset;
-static int cpu_ticks_enabled;
+static int64_t cpu_ticks_prev = 0;
+static int64_t cpu_ticks_offset = 0;
+static int cpu_ticks_enabled = 0;
 
 static inline int64_t cpu_get_ticks(void)
 {
     if (!cpu_ticks_enabled) {
         return cpu_ticks_offset;
     } else {
+       int64_t ticks = cpu_get_real_ticks();
+       if( cpu_ticks_prev > ticks )
+       {
+           cpu_ticks_offset += ( cpu_ticks_prev - ticks );
+       }
+       cpu_ticks_prev = ticks;
         return cpu_get_real_ticks() + cpu_ticks_offset;
     }
 }
#1 lore raw

I can confirm this patch applied alone to 0.7.2 did something for me. I suspended the PC for 1 minute (just to reboot the machine) and after resume, my quest win95 was still working. I will of course try longer periods later.

Peter

Quoted 71 lines · John Coiner

After I hibernate my x86 linux host machine (using the "swsusp2" package) and resume, QEMU and guest hang. That is odd, because hibernation is transparent to all other user programs.

Has anyone else noticed this? If so please try the patch.

You know QEMU is doing something weird, for it to be sensitive to hibernation. Indeed, the cause is something you don't see every day:

QEMU implements timers partly by reading the CPU's tick counter directly from hardware, with an architecture-specific assembly instruction ('rdtsc' on intel, and so on for other archs) rather than using purely OS-provided features to get the time.

The problem is that following hibernation, the CPU tick counter isn't what it used to be. On my host, it has a lower value -- it jumped backwards. This breaks QEMU's timers. Some of them decide to block for a very long time, which causes the guest to hang or behave oddly.

(Maybe this is technically a bug in "swsusp2", maybe it should restore the CPU tick counter to the previous value upon resume. But who cares. It's easy to fix this in QEMU, and it would not be easy to check and fix all hibernation schemes on all architectures and operating systems.)

The following patch fixes the 'cpu_get_ticks()' function to always return a non-decreasing value, even if the value read from hardware decreases. Hope it helps. Feedback is welcome.

-- John

vl.c 2005-10-10 00:54:08.000000000 -0400 renamed from vl.c 2005-09-04 13:11:31.000000000 -0400 +9 -2
@@ -545,14 +547,21 @@
 #error unsupported CPU
 #endif
 
-static int64_t cpu_ticks_offset;
-static int cpu_ticks_enabled;
+static int64_t cpu_ticks_prev = 0;
+static int64_t cpu_ticks_offset = 0;
+static int cpu_ticks_enabled = 0;
 
 static inline int64_t cpu_get_ticks(void)
 {
     if (!cpu_ticks_enabled) {
         return cpu_ticks_offset;
     } else {
+       int64_t ticks = cpu_get_real_ticks();
+       if( cpu_ticks_prev > ticks )
+       {
+           cpu_ticks_offset += ( cpu_ticks_prev - ticks );
+       }
+       cpu_ticks_prev = ticks;
         return cpu_get_real_ticks() + cpu_ticks_offset;
     }
 }

_______________________________________________
Qemu-devel mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/qemu-devel