@@ -352,6 +352,10 @@ static void qemu_init_sigbus(void)
{
struct sigaction action;
+ /*
+ * ALERT: when modifying this, take care that SIGBUS forwarding in
+ * os_mem_prealloc() will continue working as expected.
+ */
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = sigbus_handler;
@@ -95,6 +95,7 @@ typedef struct MemsetThread MemsetThread;
/* used by sigbus_handler() */
static MemsetContext *sigbus_memset_context;
+struct sigaction sigbus_oldact;
static QemuMutex sigbus_mutex;
static QemuMutex page_mutex;
@@ -446,7 +447,11 @@ const char *qemu_get_exec_dir(void)
return exec_dir;
}
+#ifdef CONFIG_LINUX
+static void sigbus_handler(int signal, siginfo_t *siginfo, void *ctx)
+#else /* CONFIG_LINUX */
static void sigbus_handler(int signal)
+#endif /* CONFIG_LINUX */
{
int i;
@@ -459,6 +464,26 @@ static void sigbus_handler(int signal)
}
}
}
+
+#ifdef CONFIG_LINUX
+ /*
+ * We assume that the MCE SIGBUS handler could have been registered. We
+ * should never receive BUS_MCEERR_AO on any of our threads, but only on
+ * the main thread registered for PR_MCE_KILL_EARLY. Further, we should not
+ * receive BUS_MCEERR_AR triggered by action of other threads on one of
+ * our threads. So, no need to check for unrelated SIGBUS when seeing one
+ * for our threads.
+ *
+ * We will forward to the MCE handler, which will either handle the SIGBUS
+ * or reinstall the default SIGBUS handler and reraise the SIGBUS. The
+ * default SIGBUS handler will crash the process, so we don't care.
+ */
+ if (sigbus_oldact.sa_flags & SA_SIGINFO) {
+ sigbus_oldact.sa_sigaction(signal, siginfo, ctx);
+ return;
+ }
+#endif /* CONFIG_LINUX */
+ warn_report("os_mem_prealloc: unrelated SIGBUS detected and ignored");
}
static void *do_touch_pages(void *arg)
@@ -628,10 +653,10 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
{
static gsize initialized;
int ret;
- struct sigaction act, oldact;
size_t hpagesize = qemu_fd_getpagesize(fd);
size_t numpages = DIV_ROUND_UP(memory, hpagesize);
bool use_madv_populate_write;
+ struct sigaction act;
/*
* Sense on every invocation, as MADV_POPULATE_WRITE cannot be used for
@@ -647,10 +672,15 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
qemu_mutex_lock(&sigbus_mutex);
memset(&act, 0, sizeof(act));
+#ifdef CONFIG_LINUX
+ act.sa_sigaction = &sigbus_handler;
+ act.sa_flags = SA_SIGINFO;
+#else /* CONFIG_LINUX */
act.sa_handler = &sigbus_handler;
act.sa_flags = 0;
+#endif /* CONFIG_LINUX */
- ret = sigaction(SIGBUS, &act, &oldact);
+ ret = sigaction(SIGBUS, &act, &sigbus_oldact);
if (ret) {
error_setg_errno(errp, errno,
"os_mem_prealloc: failed to install signal handler");
@@ -667,7 +697,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
}
if (!use_madv_populate_write) {
- ret = sigaction(SIGBUS, &oldact, NULL);
+ ret = sigaction(SIGBUS, &sigbus_oldact, NULL);
if (ret) {
/* Terminate QEMU since it can't recover from error */
perror("os_mem_prealloc: failed to reinstall signal handler");