Commit 2ae41db2 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

KVM: do not use sigtimedwait to catch SIGBUS



Call kvm_on_sigbus_vcpu asynchronously from the VCPU thread.
Information for the SIGBUS can be stored in thread-local variables
and processed later in kvm_cpu_exec.

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 4d39892c
Loading
Loading
Loading
Loading
+13 −18
Original line number Diff line number Diff line
@@ -926,10 +926,18 @@ static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx)
        sigbus_reraise();
    }

    if (current_cpu) {
        /* Called asynchronously in VCPU thread.  */
        if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) {
            sigbus_reraise();
        }
    } else {
        /* Called synchronously (via signalfd) in main thread.  */
        if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) {
            sigbus_reraise();
        }
    }
}

static void qemu_init_sigbus(void)
{
@@ -958,8 +966,9 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
    sigaction(SIG_IPI, &sigact, NULL);

    pthread_sigmask(SIG_BLOCK, NULL, &set);
    sigdelset(&set, SIG_IPI);
    sigdelset(&set, SIGBUS);
    pthread_sigmask(SIG_SETMASK, &set, NULL);
    sigdelset(&set, SIG_IPI);
    r = kvm_set_signal_mask(cpu, &set);
    if (r) {
        fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r));
@@ -977,7 +986,6 @@ static void qemu_kvm_eat_signals(CPUState *cpu)

    sigemptyset(&waitset);
    sigaddset(&waitset, SIG_IPI);
    sigaddset(&waitset, SIGBUS);

    do {
        r = sigtimedwait(&waitset, &siginfo, &ts);
@@ -986,25 +994,12 @@ static void qemu_kvm_eat_signals(CPUState *cpu)
            exit(1);
        }

        switch (r) {
        case SIGBUS:
            if (siginfo.si_code != BUS_MCEERR_AO && siginfo.si_code != BUS_MCEERR_AR) {
                sigbus_reraise();
            }
            if (kvm_on_sigbus_vcpu(cpu, 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));
    } while (sigismember(&chkset, SIG_IPI));
}
#else /* !CONFIG_LINUX */
static void qemu_init_sigbus(void)
+4 −1
Original line number Diff line number Diff line
@@ -357,7 +357,10 @@ bool kvm_vcpu_id_is_valid(int vcpu_id);
/* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */
unsigned long kvm_arch_vcpu_id(CPUState *cpu);

int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr);
#ifdef TARGET_I386
#define KVM_HAVE_MCE_INJECTION 1
void kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr);
#endif

void kvm_arch_init_irq_routing(KVMState *s);

+34 −1
Original line number Diff line number Diff line
@@ -1893,6 +1893,12 @@ void kvm_cpu_synchronize_post_init(CPUState *cpu)
    run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}

#ifdef KVM_HAVE_MCE_INJECTION
static __thread void *pending_sigbus_addr;
static __thread int pending_sigbus_code;
static __thread bool have_sigbus_pending;
#endif

int kvm_cpu_exec(CPUState *cpu)
{
    struct kvm_run *run = cpu->kvm_run;
@@ -1930,6 +1936,16 @@ int kvm_cpu_exec(CPUState *cpu)

        attrs = kvm_arch_post_run(cpu, run);

#ifdef KVM_HAVE_MCE_INJECTION
        if (unlikely(have_sigbus_pending)) {
            qemu_mutex_lock_iothread();
            kvm_arch_on_sigbus_vcpu(cpu, pending_sigbus_code,
                                    pending_sigbus_addr);
            have_sigbus_pending = false;
            qemu_mutex_unlock_iothread();
        }
#endif

        if (run_ret < 0) {
            if (run_ret == -EINTR || run_ret == -EAGAIN) {
                DPRINTF("io window exit\n");
@@ -2392,13 +2408,27 @@ int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset)
    return r;
}

/* Called asynchronously in VCPU thread.  */
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
{
    return kvm_arch_on_sigbus_vcpu(cpu, code, addr);
#ifdef KVM_HAVE_MCE_INJECTION
    if (have_sigbus_pending) {
        return 1;
    }
    have_sigbus_pending = true;
    pending_sigbus_addr = addr;
    pending_sigbus_code = code;
    atomic_set(&cpu->exit_request, 1);
    return 0;
#else
    return 1;
#endif
}

/* Called synchronously (via signalfd) in main thread.  */
int kvm_on_sigbus(int code, void *addr)
{
#ifdef KVM_HAVE_MCE_INJECTION
    /* Action required MCE kills the process if SIGBUS is blocked.  Because
     * that's what happens in the I/O thread, where we handle MCE via signalfd,
     * we can only get action optional here.
@@ -2406,6 +2436,9 @@ int kvm_on_sigbus(int code, void *addr)
    assert(code != BUS_MCEERR_AR);
    kvm_arch_on_sigbus_vcpu(first_cpu, code, addr);
    return 0;
#else
    return 1;
#endif
}

int kvm_create_device(KVMState *s, uint64_t type, bool test)
+0 −5
Original line number Diff line number Diff line
@@ -560,11 +560,6 @@ int kvm_arch_process_async_events(CPUState *cs)
    return 0;
}

int kvm_arch_on_sigbus_vcpu(CPUState *cs, int code, void *addr)
{
    return 1;
}

/* The #ifdef protections are until 32bit headers are imported and can
 * be removed once both 32 and 64 bit reach feature parity.
 */
+2 −3
Original line number Diff line number Diff line
@@ -455,7 +455,7 @@ static void hardware_memory_error(void)
    exit(1);
}

int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
{
    X86CPU *cpu = X86_CPU(c);
    CPUX86State *env = &cpu->env;
@@ -475,7 +475,7 @@ int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
            kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) {
            kvm_hwpoison_page_add(ram_addr);
            kvm_mce_inject(cpu, paddr, code);
            return 0;
            return;
        }

        fprintf(stderr, "Hardware memory error for memory used by "
@@ -487,7 +487,6 @@ int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
    }

    /* Hope we are lucky for AO MCE */
    return 0;
}

static int kvm_inject_mce_oldstyle(X86CPU *cpu)
Loading