Re: [patch uq/master 7/8] MCE: Relay UCR MCE to guest

Hidetoshi Seto
2010-10-06T01:11:48Z

(2010/10/05 3:54), Marcelo Tosatti wrote:
> Port qemu-kvm's
>
> commit 4b62fff1101a7ad77553147717a8bd3bf79df7ef
> Author: Huang Ying <ying.huang@intel.com>
> Date: Mon Sep 21 10:43:25 2009 +0800
>
> MCE: Relay UCR MCE to guest
>
> UCR (uncorrected recovery) MCE is supported in recent Intel CPUs,
> where some hardware error such as some memory error can be reported
> without PCC (processor context corrupted). To recover from such MCE,
> the corresponding memory will be unmapped, and all processes accessing
> the memory will be killed via SIGBUS.
>
> For KVM, if QEMU/KVM is killed, all guest processes will be killed
> too. So we relay SIGBUS from host OS to guest system via a UCR MCE
> injection. Then guest OS can isolate corresponding memory and kill
> necessary guest processes only. SIGBUS sent to main thread (not VCPU
> threads) will be broadcast to all VCPU threads as UCR MCE.
>
> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
>

(snip)

> +static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
> + void *ctx)
> +{
> +#if defined(TARGET_I386)
> + if (kvm_on_sigbus_vcpu(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr))
> +#endif
> + sigbus_reraise();
> +}
> +
> static void qemu_kvm_eat_signal(CPUState *env, int timeout)
> {
> struct timespec ts;
> int r, e;
> siginfo_t siginfo;
> sigset_t waitset;
> + sigset_t chkset;
>
> ts.tv_sec = timeout / 1000;
> ts.tv_nsec = (timeout % 1000) * 1000000;
>
> sigemptyset(&waitset);
> sigaddset(&waitset, SIG_IPI);
> + sigaddset(&waitset, SIGBUS);
>
> - qemu_mutex_unlock(&qemu_global_mutex);
> - r = sigtimedwait(&waitset, &siginfo, &ts);
> - e = errno;
> - qemu_mutex_lock(&qemu_global_mutex);
> + do {
> + qemu_mutex_unlock(&qemu_global_mutex);
>
> - if (r == -1 && !(e == EAGAIN || e == EINTR)) {
> - fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
> - exit(1);
> - }
> + r = sigtimedwait(&waitset, &siginfo, &ts);
> + e = errno;
> +
> + qemu_mutex_lock(&qemu_global_mutex);
> +
> + if (r == -1 && !(e == EAGAIN || e == EINTR)) {
> + fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
> + exit(1);
> + }
> +
> + switch (r) {
> + case SIGBUS:
> +#ifdef TARGET_I386
> + if (kvm_on_sigbus(env, siginfo.si_code, siginfo.si_addr))
> +#endif
> + sigbus_reraise();
> + break;
> + default:
> + break;
> + }
> +
> + r = sigpending(&chkset);
> + if (r == -1) {
> + fprintf(stderr, "sigpending: %s\n", strerror(e));
> + exit(1);
> + }
> + } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
> }
>
> static void qemu_kvm_wait_io_event(CPUState *env)

(snip)

> Index: qemu/kvm.h
> ===================================================================
> --- qemu.orig/kvm.h
> +++ qemu/kvm.h
> @@ -110,6 +110,9 @@ int kvm_arch_init_vcpu(CPUState *env);
>
> void kvm_arch_reset_vcpu(CPUState *env);
>
> +int kvm_on_sigbus(CPUState *env, int code, void *addr);
> +int kvm_on_sigbus_vcpu(int code, void *addr);
> +
> struct kvm_guest_debug;
> struct kvm_debug_exit_arch;
>

So kvm_on_sigbus() is called from qemu_kvm_eat_signal() that is
called on vcpu thread, while kvm_on_sigbus_vcpu() is called via
sigbus_handler that invoked on iothread using signalfd.

... Inverse naming?

Thanks,
H.Seto