Patchwork [07/18] kvm: Add MCE signal support for !CONFIG_IOTHREAD

login
register
mail settings
Submitter Jan Kiszka
Date Jan. 10, 2011, 8:32 a.m.
Message ID <39b9c54e6cd122cb613f5fb79502eb053609d237.1294648329.git.jan.kiszka@web.de>
Download mbox | patch
Permalink /patch/78101/
State New
Headers show

Comments

Jan Kiszka - Jan. 10, 2011, 8:32 a.m.
From: Jan Kiszka <jan.kiszka@siemens.com>

Currently, we only configure and process MCE-related SIGBUS events if
CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
handler registration and system configuration. Make sure that events
happening over a VCPU context in non-threaded mode get dispatched as
VCPU MCEs.

We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
move it (unmodified) and add the required Windows stub.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
CC: Huang Ying <ying.huang@intel.com>
---
 cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 124 insertions(+), 76 deletions(-)
Marcelo Tosatti - Jan. 24, 2011, 11:17 a.m.
On Mon, Jan 10, 2011 at 09:32:00AM +0100, Jan Kiszka wrote:
> From: Jan Kiszka <jan.kiszka@siemens.com>
> 
> Currently, we only configure and process MCE-related SIGBUS events if
> CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
> handler registration and system configuration. Make sure that events
> happening over a VCPU context in non-threaded mode get dispatched as
> VCPU MCEs.
> 
> We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
> move it (unmodified) and add the required Windows stub.
> 
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> CC: Huang Ying <ying.huang@intel.com>
> ---
>  cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
>  1 files changed, 124 insertions(+), 76 deletions(-)
> 
> diff --git a/cpus.c b/cpus.c
> index 6da0f8f..b6f1cfb 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -34,9 +34,6 @@
>  
>  #include "cpus.h"
>  #include "compatfd.h"
> -#ifdef CONFIG_LINUX
> -#include <sys/prctl.h>
> -#endif
>  
>  #ifdef SIGRTMIN
>  #define SIG_IPI (SIGRTMIN+4)
> @@ -44,10 +41,24 @@
>  #define SIG_IPI SIGUSR1
>  #endif
>  

> @@ -912,6 +954,8 @@ static int qemu_cpu_exec(CPUState *env)
>  
>  bool cpu_exec_all(void)
>  {
> +    int r;
> +
>      if (next_cpu == NULL)
>          next_cpu = first_cpu;
>      for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
> @@ -923,7 +967,11 @@ bool cpu_exec_all(void)
>          if (qemu_alarm_pending())
>              break;
>          if (cpu_can_run(env)) {
> -            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
> +            r = qemu_cpu_exec(env);
> +            if (kvm_enabled()) {
> +                qemu_kvm_eat_signals(env);
> +            }
> +            if (r == EXCP_DEBUG) {
>                  break;
>              }

SIGBUS should be processed outside of vcpu execution context, think of a
non MCE SIGBUS while vm is stopped. Could use signalfd for that.

But the SIGBUS handler for !IOTHREAD case should not ignore Action
Required, since it might have been generated in vcpu context.
Jan Kiszka - Jan. 24, 2011, 12:36 p.m.
On 2011-01-24 12:17, Marcelo Tosatti wrote:
> On Mon, Jan 10, 2011 at 09:32:00AM +0100, Jan Kiszka wrote:
>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>
>> Currently, we only configure and process MCE-related SIGBUS events if
>> CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
>> handler registration and system configuration. Make sure that events
>> happening over a VCPU context in non-threaded mode get dispatched as
>> VCPU MCEs.
>>
>> We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
>> move it (unmodified) and add the required Windows stub.
>>
>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>> CC: Huang Ying <ying.huang@intel.com>
>> ---
>>  cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
>>  1 files changed, 124 insertions(+), 76 deletions(-)
>>
>> diff --git a/cpus.c b/cpus.c
>> index 6da0f8f..b6f1cfb 100644
>> --- a/cpus.c
>> +++ b/cpus.c
>> @@ -34,9 +34,6 @@
>>  
>>  #include "cpus.h"
>>  #include "compatfd.h"
>> -#ifdef CONFIG_LINUX
>> -#include <sys/prctl.h>
>> -#endif
>>  
>>  #ifdef SIGRTMIN
>>  #define SIG_IPI (SIGRTMIN+4)
>> @@ -44,10 +41,24 @@
>>  #define SIG_IPI SIGUSR1
>>  #endif
>>  
> 
>> @@ -912,6 +954,8 @@ static int qemu_cpu_exec(CPUState *env)
>>  
>>  bool cpu_exec_all(void)
>>  {
>> +    int r;
>> +
>>      if (next_cpu == NULL)
>>          next_cpu = first_cpu;
>>      for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
>> @@ -923,7 +967,11 @@ bool cpu_exec_all(void)
>>          if (qemu_alarm_pending())
>>              break;
>>          if (cpu_can_run(env)) {
>> -            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
>> +            r = qemu_cpu_exec(env);
>> +            if (kvm_enabled()) {
>> +                qemu_kvm_eat_signals(env);
>> +            }
>> +            if (r == EXCP_DEBUG) {
>>                  break;
>>              }
> 
> SIGBUS should be processed outside of vcpu execution context, think of a
> non MCE SIGBUS while vm is stopped. Could use signalfd for that.

signalfd - that's the missing bit. I was thinking of how to handle
SIGBUS events raised outside the vcpu context. We need to handle them
synchronously, and signalfd should allow this.

> 
> But the SIGBUS handler for !IOTHREAD case should not ignore Action
> Required, since it might have been generated in vcpu context.
> 

Yes, the sigbus handler will require some rework when we actually start
using it for !IOTHREAD.

Will have a look, thanks,
Jan
Jan Kiszka - Jan. 26, 2011, 8:09 a.m.
On 2011-01-24 13:36, Jan Kiszka wrote:
> On 2011-01-24 12:17, Marcelo Tosatti wrote:
>> On Mon, Jan 10, 2011 at 09:32:00AM +0100, Jan Kiszka wrote:
>>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>>
>>> Currently, we only configure and process MCE-related SIGBUS events if
>>> CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
>>> handler registration and system configuration. Make sure that events
>>> happening over a VCPU context in non-threaded mode get dispatched as
>>> VCPU MCEs.
>>>
>>> We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
>>> move it (unmodified) and add the required Windows stub.
>>>
>>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>>> CC: Huang Ying <ying.huang@intel.com>
>>> ---
>>>  cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
>>>  1 files changed, 124 insertions(+), 76 deletions(-)
>>>
>>> diff --git a/cpus.c b/cpus.c
>>> index 6da0f8f..b6f1cfb 100644
>>> --- a/cpus.c
>>> +++ b/cpus.c
>>> @@ -34,9 +34,6 @@
>>>  
>>>  #include "cpus.h"
>>>  #include "compatfd.h"
>>> -#ifdef CONFIG_LINUX
>>> -#include <sys/prctl.h>
>>> -#endif
>>>  
>>>  #ifdef SIGRTMIN
>>>  #define SIG_IPI (SIGRTMIN+4)
>>> @@ -44,10 +41,24 @@
>>>  #define SIG_IPI SIGUSR1
>>>  #endif
>>>  
>>
>>> @@ -912,6 +954,8 @@ static int qemu_cpu_exec(CPUState *env)
>>>  
>>>  bool cpu_exec_all(void)
>>>  {
>>> +    int r;
>>> +
>>>      if (next_cpu == NULL)
>>>          next_cpu = first_cpu;
>>>      for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
>>> @@ -923,7 +967,11 @@ bool cpu_exec_all(void)
>>>          if (qemu_alarm_pending())
>>>              break;
>>>          if (cpu_can_run(env)) {
>>> -            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
>>> +            r = qemu_cpu_exec(env);
>>> +            if (kvm_enabled()) {
>>> +                qemu_kvm_eat_signals(env);
>>> +            }
>>> +            if (r == EXCP_DEBUG) {
>>>                  break;
>>>              }
>>
>> SIGBUS should be processed outside of vcpu execution context, think of a
>> non MCE SIGBUS while vm is stopped. Could use signalfd for that.
> 
> signalfd - that's the missing bit. I was thinking of how to handle
> SIGBUS events raised outside the vcpu context. We need to handle them
> synchronously, and signalfd should allow this.

This was straightforward. But now I wonder what actually makes this
pattern work. Doesn't the kernel force-inject SIGBUS, i.e. ignores any
blocking? Or does this only apply to BUS_MCEERR_AR?

> 
>>
>> But the SIGBUS handler for !IOTHREAD case should not ignore Action
>> Required, since it might have been generated in vcpu context.
>>
> 
> Yes, the sigbus handler will require some rework when we actually start
> using it for !IOTHREAD.

And this no longer makes sense to me. The current version simply uses
the same sigbus handler for both modes, an that forwards the error code
properly. What did you mean?

Jan
Marcelo Tosatti - Jan. 26, 2011, 12:01 p.m.
On Wed, Jan 26, 2011 at 09:09:25AM +0100, Jan Kiszka wrote:
> On 2011-01-24 13:36, Jan Kiszka wrote:
> > On 2011-01-24 12:17, Marcelo Tosatti wrote:
> >> On Mon, Jan 10, 2011 at 09:32:00AM +0100, Jan Kiszka wrote:
> >>> From: Jan Kiszka <jan.kiszka@siemens.com>
> >>>
> >>> Currently, we only configure and process MCE-related SIGBUS events if
> >>> CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
> >>> handler registration and system configuration. Make sure that events
> >>> happening over a VCPU context in non-threaded mode get dispatched as
> >>> VCPU MCEs.
> >>>
> >>> We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
> >>> move it (unmodified) and add the required Windows stub.
> >>>
> >>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> >>> CC: Huang Ying <ying.huang@intel.com>
> >>> ---
> >>>  cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
> >>>  1 files changed, 124 insertions(+), 76 deletions(-)
> >>>
> >>> diff --git a/cpus.c b/cpus.c
> >>> index 6da0f8f..b6f1cfb 100644
> >>> --- a/cpus.c
> >>> +++ b/cpus.c
> >>> @@ -34,9 +34,6 @@
> >>>  
> >>>  #include "cpus.h"
> >>>  #include "compatfd.h"
> >>> -#ifdef CONFIG_LINUX
> >>> -#include <sys/prctl.h>
> >>> -#endif
> >>>  
> >>>  #ifdef SIGRTMIN
> >>>  #define SIG_IPI (SIGRTMIN+4)
> >>> @@ -44,10 +41,24 @@
> >>>  #define SIG_IPI SIGUSR1
> >>>  #endif
> >>>  
> >>
> >>> @@ -912,6 +954,8 @@ static int qemu_cpu_exec(CPUState *env)
> >>>  
> >>>  bool cpu_exec_all(void)
> >>>  {
> >>> +    int r;
> >>> +
> >>>      if (next_cpu == NULL)
> >>>          next_cpu = first_cpu;
> >>>      for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
> >>> @@ -923,7 +967,11 @@ bool cpu_exec_all(void)
> >>>          if (qemu_alarm_pending())
> >>>              break;
> >>>          if (cpu_can_run(env)) {
> >>> -            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
> >>> +            r = qemu_cpu_exec(env);
> >>> +            if (kvm_enabled()) {
> >>> +                qemu_kvm_eat_signals(env);
> >>> +            }
> >>> +            if (r == EXCP_DEBUG) {
> >>>                  break;
> >>>              }
> >>
> >> SIGBUS should be processed outside of vcpu execution context, think of a
> >> non MCE SIGBUS while vm is stopped. Could use signalfd for that.
> > 
> > signalfd - that's the missing bit. I was thinking of how to handle
> > SIGBUS events raised outside the vcpu context. We need to handle them
> > synchronously, and signalfd should allow this.
> 
> This was straightforward. But now I wonder what actually makes this
> pattern work. Doesn't the kernel force-inject SIGBUS, i.e. ignores any
> blocking? Or does this only apply to BUS_MCEERR_AR?

SIGBUS is only forced if BUS_MCEERR_AR and the poisoned memory was not accessed 
on behalf of the guest (say directly by qemu).

> >> But the SIGBUS handler for !IOTHREAD case should not ignore Action
> >> Required, since it might have been generated in vcpu context.
> >>
> > 
> > Yes, the sigbus handler will require some rework when we actually start
> > using it for !IOTHREAD.
> 
> And this no longer makes sense to me. The current version simply uses
> the same sigbus handler for both modes, an that forwards the error code
> properly. What did you mean?

There are two handlers, kvm_on_sigbus and kvm_on_sigbus_vcpu.
kvm_on_sigbus, the handler for iothread, dies on BUS_MCEERR_AR (which
will be generated if poisoned memory is accessed on behalf of vcpu). It
should be handled with !CONFIG_IOTHREAD.
Jan Kiszka - Jan. 26, 2011, 12:06 p.m.
On 2011-01-26 13:01, Marcelo Tosatti wrote:
> On Wed, Jan 26, 2011 at 09:09:25AM +0100, Jan Kiszka wrote:
>> On 2011-01-24 13:36, Jan Kiszka wrote:
>>> On 2011-01-24 12:17, Marcelo Tosatti wrote:
>>>> On Mon, Jan 10, 2011 at 09:32:00AM +0100, Jan Kiszka wrote:
>>>>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>>>>
>>>>> Currently, we only configure and process MCE-related SIGBUS events if
>>>>> CONFIG_IOTHREAD is enabled. Fix this by factoring out the required
>>>>> handler registration and system configuration. Make sure that events
>>>>> happening over a VCPU context in non-threaded mode get dispatched as
>>>>> VCPU MCEs.
>>>>>
>>>>> We also need to call qemu_kvm_eat_signals in non-threaded mode now, so
>>>>> move it (unmodified) and add the required Windows stub.
>>>>>
>>>>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>>>>> CC: Huang Ying <ying.huang@intel.com>
>>>>> ---
>>>>>  cpus.c |  200 +++++++++++++++++++++++++++++++++++++++------------------------
>>>>>  1 files changed, 124 insertions(+), 76 deletions(-)
>>>>>
>>>>> diff --git a/cpus.c b/cpus.c
>>>>> index 6da0f8f..b6f1cfb 100644
>>>>> --- a/cpus.c
>>>>> +++ b/cpus.c
>>>>> @@ -34,9 +34,6 @@
>>>>>  
>>>>>  #include "cpus.h"
>>>>>  #include "compatfd.h"
>>>>> -#ifdef CONFIG_LINUX
>>>>> -#include <sys/prctl.h>
>>>>> -#endif
>>>>>  
>>>>>  #ifdef SIGRTMIN
>>>>>  #define SIG_IPI (SIGRTMIN+4)
>>>>> @@ -44,10 +41,24 @@
>>>>>  #define SIG_IPI SIGUSR1
>>>>>  #endif
>>>>>  
>>>>
>>>>> @@ -912,6 +954,8 @@ static int qemu_cpu_exec(CPUState *env)
>>>>>  
>>>>>  bool cpu_exec_all(void)
>>>>>  {
>>>>> +    int r;
>>>>> +
>>>>>      if (next_cpu == NULL)
>>>>>          next_cpu = first_cpu;
>>>>>      for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
>>>>> @@ -923,7 +967,11 @@ bool cpu_exec_all(void)
>>>>>          if (qemu_alarm_pending())
>>>>>              break;
>>>>>          if (cpu_can_run(env)) {
>>>>> -            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
>>>>> +            r = qemu_cpu_exec(env);
>>>>> +            if (kvm_enabled()) {
>>>>> +                qemu_kvm_eat_signals(env);
>>>>> +            }
>>>>> +            if (r == EXCP_DEBUG) {
>>>>>                  break;
>>>>>              }
>>>>
>>>> SIGBUS should be processed outside of vcpu execution context, think of a
>>>> non MCE SIGBUS while vm is stopped. Could use signalfd for that.
>>>
>>> signalfd - that's the missing bit. I was thinking of how to handle
>>> SIGBUS events raised outside the vcpu context. We need to handle them
>>> synchronously, and signalfd should allow this.
>>
>> This was straightforward. But now I wonder what actually makes this
>> pattern work. Doesn't the kernel force-inject SIGBUS, i.e. ignores any
>> blocking? Or does this only apply to BUS_MCEERR_AR?
> 
> SIGBUS is only forced if BUS_MCEERR_AR and the poisoned memory was not accessed 
> on behalf of the guest (say directly by qemu).

OK. I didn't find this detail in the kernel code on first glance, only
force_sig(SIGBUS, current).

> 
>>>> But the SIGBUS handler for !IOTHREAD case should not ignore Action
>>>> Required, since it might have been generated in vcpu context.
>>>>
>>>
>>> Yes, the sigbus handler will require some rework when we actually start
>>> using it for !IOTHREAD.
>>
>> And this no longer makes sense to me. The current version simply uses
>> the same sigbus handler for both modes, an that forwards the error code
>> properly. What did you mean?
> 
> There are two handlers, kvm_on_sigbus and kvm_on_sigbus_vcpu.
> kvm_on_sigbus, the handler for iothread, dies on BUS_MCEERR_AR (which
> will be generated if poisoned memory is accessed on behalf of vcpu). It
> should be handled with !CONFIG_IOTHREAD.

Just as with iothread, vcpu-triggered SIGBUS events are processes by
qemu_kvm_eat_signals, not the signal handler.

Jan

Patch

diff --git a/cpus.c b/cpus.c
index 6da0f8f..b6f1cfb 100644
--- a/cpus.c
+++ b/cpus.c
@@ -34,9 +34,6 @@ 
 
 #include "cpus.h"
 #include "compatfd.h"
-#ifdef CONFIG_LINUX
-#include <sys/prctl.h>
-#endif
 
 #ifdef SIGRTMIN
 #define SIG_IPI (SIGRTMIN+4)
@@ -44,10 +41,24 @@ 
 #define SIG_IPI SIGUSR1
 #endif
 
+#ifdef CONFIG_LINUX
+
+#include <sys/prctl.h>
+
 #ifndef PR_MCE_KILL
 #define PR_MCE_KILL 33
 #endif
 
+#ifndef PR_MCE_KILL_SET
+#define PR_MCE_KILL_SET 1
+#endif
+
+#ifndef PR_MCE_KILL_EARLY
+#define PR_MCE_KILL_EARLY 1
+#endif
+
+#endif /* CONFIG_LINUX */
+
 static CPUState *next_cpu;
 
 /***********************************************************/
@@ -158,6 +169,62 @@  static void cpu_debug_handler(CPUState *env)
     vm_stop(EXCP_DEBUG);
 }
 
+#ifdef CONFIG_LINUX
+static void sigbus_reraise(void)
+{
+    sigset_t set;
+    struct sigaction action;
+
+    memset(&action, 0, sizeof(action));
+    action.sa_handler = SIG_DFL;
+    if (!sigaction(SIGBUS, &action, NULL)) {
+        raise(SIGBUS);
+        sigemptyset(&set);
+        sigaddset(&set, SIGBUS);
+        sigprocmask(SIG_UNBLOCK, &set, NULL);
+    }
+    perror("Failed to re-raise SIGBUS!\n");
+    abort();
+}
+
+static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
+                           void *ctx)
+{
+#ifndef CONFIG_IOTHREAD
+    if (cpu_single_env) {
+        if (kvm_on_sigbus_vcpu(cpu_single_env, siginfo->ssi_code,
+                               (void *)(intptr_t)siginfo->ssi_addr)) {
+            sigbus_reraise();
+        }
+        return;
+    }
+#endif
+
+    if (kvm_on_sigbus(siginfo->ssi_code,
+                      (void *)(intptr_t)siginfo->ssi_addr)) {
+        sigbus_reraise();
+    }
+}
+
+static void qemu_init_sigbus(void)
+{
+    struct sigaction action;
+
+    memset(&action, 0, sizeof(action));
+    action.sa_flags = SA_SIGINFO;
+    action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
+    sigaction(SIGBUS, &action, NULL);
+
+    prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
+}
+
+#else /* !CONFIG_LINUX */
+
+static void qemu_init_sigbus(void)
+{
+}
+#endif /* !CONFIG_LINUX */
+
 #ifndef _WIN32
 static int io_thread_fd = -1;
 
@@ -254,6 +321,43 @@  static void qemu_kvm_init_cpu_signals(CPUState *env)
     }
 }
 
+static void qemu_kvm_eat_signals(CPUState *env)
+{
+    struct timespec ts = { 0, 0 };
+    siginfo_t siginfo;
+    sigset_t waitset;
+    sigset_t chkset;
+    int r;
+
+    sigemptyset(&waitset);
+    sigaddset(&waitset, SIG_IPI);
+    sigaddset(&waitset, SIGBUS);
+
+    do {
+        r = sigtimedwait(&waitset, &siginfo, &ts);
+        if (r == -1 && !(errno == EAGAIN || errno == EINTR)) {
+            perror("sigtimedwait");
+            exit(1);
+        }
+
+        switch (r) {
+        case SIGBUS:
+            if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) {
+                sigbus_reraise();
+            }
+            break;
+        default:
+            break;
+        }
+
+        r = sigpending(&chkset);
+        if (r == -1) {
+            perror("sigpending");
+            exit(1);
+        }
+    } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
+}
+
 #else /* _WIN32 */
 
 HANDLE qemu_event_handle;
@@ -285,6 +389,10 @@  static void qemu_event_increment(void)
 static void qemu_kvm_init_cpu_signals(CPUState *env)
 {
 }
+
+static void qemu_kvm_eat_signals(CPUState *env)
+{
+}
 #endif /* _WIN32 */
 
 #ifndef CONFIG_IOTHREAD
@@ -292,6 +400,8 @@  int qemu_init_main_loop(void)
 {
     cpu_set_debug_excp_handler(cpu_debug_handler);
 
+    qemu_init_sigbus();
+
     return qemu_event_init();
 }
 
@@ -432,13 +542,9 @@  static void qemu_tcg_init_cpu_signals(void)
     pthread_sigmask(SIG_UNBLOCK, &set, NULL);
 }
 
-static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
-                           void *ctx);
-
 static sigset_t block_io_signals(void)
 {
     sigset_t set;
-    struct sigaction action;
 
     /* SIGUSR2 used by posix-aio-compat.c */
     sigemptyset(&set);
@@ -449,15 +555,11 @@  static sigset_t block_io_signals(void)
     sigaddset(&set, SIGIO);
     sigaddset(&set, SIGALRM);
     sigaddset(&set, SIG_IPI);
+#ifdef CONFIG_LINUX
     sigaddset(&set, SIGBUS);
+#endif
     pthread_sigmask(SIG_BLOCK, &set, NULL);
 
-    memset(&action, 0, sizeof(action));
-    action.sa_flags = SA_SIGINFO;
-    action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
-    sigaction(SIGBUS, &action, NULL);
-    prctl(PR_MCE_KILL, 1, 1, 0, 0);
-
     return set;
 }
 
@@ -486,6 +588,8 @@  int qemu_init_main_loop(void)
 
     cpu_set_debug_excp_handler(cpu_debug_handler);
 
+    qemu_init_sigbus();
+
     blocked_signals = block_io_signals();
 
     ret = qemu_signalfd_init(blocked_signals);
@@ -592,68 +696,6 @@  static void qemu_tcg_wait_io_event(void)
     }
 }
 
-static void sigbus_reraise(void)
-{
-    sigset_t set;
-    struct sigaction action;
-
-    memset(&action, 0, sizeof(action));
-    action.sa_handler = SIG_DFL;
-    if (!sigaction(SIGBUS, &action, NULL)) {
-        raise(SIGBUS);
-        sigemptyset(&set);
-        sigaddset(&set, SIGBUS);
-        sigprocmask(SIG_UNBLOCK, &set, NULL);
-    }
-    perror("Failed to re-raise SIGBUS!\n");
-    abort();
-}
-
-static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
-                           void *ctx)
-{
-    if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr)) {
-        sigbus_reraise();
-    }
-}
-
-static void qemu_kvm_eat_signals(CPUState *env)
-{
-    struct timespec ts = { 0, 0 };
-    siginfo_t siginfo;
-    sigset_t waitset;
-    sigset_t chkset;
-    int r;
-
-    sigemptyset(&waitset);
-    sigaddset(&waitset, SIG_IPI);
-    sigaddset(&waitset, SIGBUS);
-
-    do {
-        r = sigtimedwait(&waitset, &siginfo, &ts);
-        if (r == -1 && !(errno == EAGAIN || errno == EINTR)) {
-            perror("sigtimedwait");
-            exit(1);
-        }
-
-        switch (r) {
-        case SIGBUS:
-            if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) {
-                sigbus_reraise();
-            }
-            break;
-        default:
-            break;
-        }
-
-        r = sigpending(&chkset);
-        if (r == -1) {
-            perror("sigpending");
-            exit(1);
-        }
-    } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
-}
-
 static void qemu_kvm_wait_io_event(CPUState *env)
 {
     while (!cpu_has_work(env))
@@ -912,6 +954,8 @@  static int qemu_cpu_exec(CPUState *env)
 
 bool cpu_exec_all(void)
 {
+    int r;
+
     if (next_cpu == NULL)
         next_cpu = first_cpu;
     for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
@@ -923,7 +967,11 @@  bool cpu_exec_all(void)
         if (qemu_alarm_pending())
             break;
         if (cpu_can_run(env)) {
-            if (qemu_cpu_exec(env) == EXCP_DEBUG) {
+            r = qemu_cpu_exec(env);
+            if (kvm_enabled()) {
+                qemu_kvm_eat_signals(env);
+            }
+            if (r == EXCP_DEBUG) {
                 break;
             }
         } else if (env->stop) {