From: Jan Kiszka <jan.kiszka@siemens.com>
To: kvm-devel <kvm@vger.kernel.org>
Cc: "Yang, Sheng" <sheng.yang@intel.com>, Avi Kivity <avi@redhat.com>,
Gleb Natapov <gleb@qumranet.com>
Subject: [PATCH 11/11] kvm: Enable NMI support for userspace irqchip
Date: Mon, 22 Sep 2008 09:59:09 +0200 [thread overview]
Message-ID: <48D7504D.1000600@siemens.com> (raw)
In-Reply-To: <48D74CE6.5060008@siemens.com>
Make use of the new KVM_NMI IOCTL to push NMIs into the KVM guest if the
user space APIC emulation or some other source raised them.
In order to use the 'nmi' monitor command which asynchroniously injects
NMIs for the given CPU, a new service called kvm_inject_interrupt is
required. This will invoke cpu_interrupt on the target VCPU, working
around the fact that the QEMU service is not thread-safe.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
libkvm/libkvm.c | 31 +++++++++++++++++++++++++++++++
libkvm/libkvm.h | 23 +++++++++++++++++++++++
qemu/monitor.c | 5 ++++-
qemu/qemu-kvm-x86.c | 26 +++++++++++++++++++++++---
qemu/qemu-kvm.c | 18 +++++++++++++++++-
qemu/qemu-kvm.h | 2 ++
6 files changed, 100 insertions(+), 5 deletions(-)
Index: b/libkvm/libkvm.c
===================================================================
--- a/libkvm/libkvm.c
+++ b/libkvm/libkvm.c
@@ -811,6 +811,11 @@ int try_push_interrupts(kvm_context_t kv
return kvm->callbacks->try_push_interrupts(kvm->opaque);
}
+int try_push_nmi(kvm_context_t kvm)
+{
+ return kvm->callbacks->try_push_nmi(kvm->opaque);
+}
+
void post_kvm_run(kvm_context_t kvm, int vcpu)
{
kvm->callbacks->post_kvm_run(kvm->opaque, vcpu);
@@ -835,6 +840,17 @@ int kvm_is_ready_for_interrupt_injection
return run->ready_for_interrupt_injection;
}
+int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_NMI
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->ready_for_nmi_injection;
+#else
+ return 0;
+#endif
+}
+
int kvm_run(kvm_context_t kvm, int vcpu)
{
int r;
@@ -842,6 +858,9 @@ int kvm_run(kvm_context_t kvm, int vcpu)
struct kvm_run *run = kvm->run[vcpu];
again:
+#ifdef KVM_CAP_NMI
+ run->request_nmi_window = try_push_nmi(kvm);
+#endif
#if !defined(__s390__)
if (!kvm->irqchip_in_kernel)
run->request_interrupt_window = try_push_interrupts(kvm);
@@ -917,6 +936,9 @@ again:
r = handle_halt(kvm, vcpu);
break;
case KVM_EXIT_IRQ_WINDOW_OPEN:
+#ifdef KVM_CAP_NMI
+ case KVM_EXIT_NMI_WINDOW_OPEN:
+#endif
break;
case KVM_EXIT_SHUTDOWN:
r = handle_shutdown(kvm, vcpu);
@@ -1001,6 +1023,15 @@ int kvm_has_sync_mmu(kvm_context_t kvm)
return r;
}
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_NMI
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI);
+#else
+ return -ENOSYS;
+#endif
+}
+
int kvm_init_coalesced_mmio(kvm_context_t kvm)
{
int r = 0;
Index: b/libkvm/libkvm.h
===================================================================
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -66,6 +66,7 @@ struct kvm_callbacks {
int (*shutdown)(void *opaque, int vcpu);
int (*io_window)(void *opaque);
int (*try_push_interrupts)(void *opaque);
+ int (*try_push_nmi)(void *opaque);
void (*post_kvm_run)(void *opaque, int vcpu);
int (*pre_kvm_run)(void *opaque, int vcpu);
int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write);
@@ -216,6 +217,17 @@ uint64_t kvm_get_apic_base(kvm_context_t
int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu);
/*!
+ * \brief Check if a vcpu is ready for NMI injection
+ *
+ * This checks if vcpu is not already running in NMI context.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return boolean indicating NMI injection readiness
+ */
+int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu);
+
+/*!
* \brief Read VCPU registers
*
* This gets the GP registers from the VCPU and outputs them
@@ -579,6 +591,17 @@ int kvm_set_lapic(kvm_context_t kvm, int
#endif
+/*!
+ * \brief Simulate an NMI
+ *
+ * This allows you to simulate a non-maskable interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu);
+
#endif
/*!
Index: b/qemu/qemu-kvm-x86.c
===================================================================
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -598,7 +598,8 @@ int kvm_arch_halt(void *opaque, int vcpu
CPUState *env = cpu_single_env;
if (!((env->interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK))) {
+ (env->eflags & IF_MASK)) &&
+ !(env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 1;
env->exception_index = EXCP_HLT;
}
@@ -627,8 +628,9 @@ void kvm_arch_post_kvm_run(void *opaque,
int kvm_arch_has_work(CPUState *env)
{
- if ((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) &&
- (env->eflags & IF_MASK))
+ if (((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI))
return 1;
return 0;
}
@@ -653,6 +655,24 @@ int kvm_arch_try_push_interrupts(void *o
return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0;
}
+int kvm_arch_try_push_nmi(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r;
+
+ if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI)))
+ return 0;
+
+ if (kvm_is_ready_for_nmi_injection(kvm_context, env->cpu_index)) {
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ r = kvm_inject_nmi(kvm_context, env->cpu_index);
+ if (r < 0)
+ printf("cpu %d fail inject NMI\n", env->cpu_index);
+ }
+
+ return (env->interrupt_request & CPU_INTERRUPT_NMI) != 0;
+}
+
void kvm_arch_update_regs_for_sipi(CPUState *env)
{
SegmentCache cs = env->segs[R_CS];
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -128,6 +128,16 @@ static void on_vcpu(CPUState *env, void
qemu_cond_wait(&qemu_work_cond);
}
+static void inject_interrupt(void *data)
+{
+ cpu_interrupt(vcpu->env, (int)data);
+}
+
+void kvm_inject_interrupt(CPUState *env, int mask)
+{
+ on_vcpu(env, inject_interrupt, (void *)mask);
+}
+
void kvm_update_interrupt_request(CPUState *env)
{
int signal = 0;
@@ -166,6 +176,11 @@ static int try_push_interrupts(void *opa
return kvm_arch_try_push_interrupts(opaque);
}
+static int try_push_nmi(void *opaque)
+{
+ return kvm_arch_try_push_nmi(opaque);
+}
+
static void post_kvm_run(void *opaque, int vcpu)
{
@@ -397,7 +412,7 @@ static int kvm_main_loop_cpu(CPUState *e
while (1) {
while (!has_work(env))
kvm_main_loop_wait(env, 1000);
- if (env->interrupt_request & CPU_INTERRUPT_HARD)
+ if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI))
env->halted = 0;
if (!kvm_irqchip_in_kernel(kvm_context) && info->sipi_needed)
update_regs_for_sipi(env);
@@ -719,6 +734,7 @@ static struct kvm_callbacks qemu_kvm_ops
.shutdown = kvm_shutdown,
.io_window = kvm_io_window,
.try_push_interrupts = try_push_interrupts,
+ .try_push_nmi = try_push_nmi,
.post_kvm_run = post_kvm_run,
.pre_kvm_run = pre_kvm_run,
#ifdef TARGET_I386
Index: b/qemu/qemu-kvm.h
===================================================================
--- a/qemu/qemu-kvm.h
+++ b/qemu/qemu-kvm.h
@@ -35,6 +35,7 @@ int kvm_get_phys_ram_page_bitmap(unsigne
void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env);
void qemu_kvm_cpuid_on_env(CPUState *env);
+void kvm_inject_interrupt(CPUState *env, int mask);
void kvm_update_after_sipi(CPUState *env);
void kvm_update_interrupt_request(CPUState *env);
void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr,
@@ -57,6 +58,7 @@ void kvm_arch_pre_kvm_run(void *opaque,
void kvm_arch_post_kvm_run(void *opaque, int vcpu);
int kvm_arch_has_work(CPUState *env);
int kvm_arch_try_push_interrupts(void *opaque);
+int kvm_arch_try_push_nmi(void *opaque);
void kvm_arch_update_regs_for_sipi(CPUState *env);
void kvm_arch_cpu_reset(CPUState *env);
Index: b/qemu/monitor.c
===================================================================
--- a/qemu/monitor.c
+++ b/qemu/monitor.c
@@ -1399,7 +1399,10 @@ static void do_inject_nmi(int cpu_index)
for (env = first_cpu; env != NULL; env = env->next_cpu)
if (env->cpu_index == cpu_index) {
- cpu_interrupt(env, CPU_INTERRUPT_NMI);
+ if (kvm_enabled())
+ kvm_inject_interrupt(env, CPU_INTERRUPT_NMI);
+ else
+ cpu_interrupt(env, CPU_INTERRUPT_NMI);
break;
}
}
next prev parent reply other threads:[~2008-09-22 8:03 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-09-22 7:44 [PATCH 0/11] Fix&Enhance NMI support for KVM - v3 Jan Kiszka
2008-09-22 7:58 ` [PATCH 1/11] VMX: include all IRQ window exits in statistics Jan Kiszka
2008-09-22 7:58 ` [PATCH 2/11] VMX: refactor/fix IRQ and NMI injectability determination Jan Kiszka
2008-09-22 7:58 ` [PATCH 3/11] VMX: refactor IRQ and NMI window enabling Jan Kiszka
2008-09-22 7:58 ` [PATCH 5/11] kvm-x86: Enable NMI Watchdog via in-kernel PIT source Jan Kiszka
2008-09-22 7:58 ` [PATCH 6/11] kvm-x86: VCPU with pending NMI is runnabled Jan Kiszka
2008-09-22 7:58 ` [PATCH 7/11] kvm: kick NMI receiving VCPU Jan Kiszka
2008-09-22 7:58 ` [PATCH 8/11] kvm-x86: Support for user space injected NMIs Jan Kiszka
2008-09-22 7:59 ` [PATCH 9/11] VMX: Provide support " Jan Kiszka
2008-09-23 6:28 ` Yang, Sheng
2008-09-23 15:42 ` Jan Kiszka
2008-09-22 7:59 ` [PATCH 10/11] VMX: work around lacking VNMI support Jan Kiszka
2008-09-22 14:15 ` Gleb Natapov
2008-09-23 8:46 ` Jan Kiszka
2008-09-23 8:50 ` Gleb Natapov
2008-09-23 8:57 ` Jan Kiszka
2008-09-23 9:00 ` Gleb Natapov
2008-09-23 9:08 ` Yang, Sheng
2008-09-23 9:15 ` Gleb Natapov
2008-09-23 9:24 ` Yang, Sheng
2008-09-23 9:26 ` Gleb Natapov
2008-09-23 9:37 ` Yang, Sheng
2008-09-23 9:42 ` Yang, Sheng
2008-09-23 9:45 ` Gleb Natapov
2008-09-23 9:50 ` Yang, Sheng
2008-09-24 12:40 ` Jan Kiszka
2008-09-24 12:44 ` Avi Kivity
2008-09-24 12:50 ` Gleb Natapov
2008-09-24 12:56 ` Jan Kiszka
2008-09-24 13:02 ` Gleb Natapov
2008-09-24 13:08 ` Jan Kiszka
2008-09-24 13:24 ` Gleb Natapov
2008-09-24 13:33 ` Jan Kiszka
2008-09-24 13:35 ` Gleb Natapov
2008-09-24 13:35 ` Avi Kivity
2008-09-24 14:07 ` Jan Kiszka
2008-09-24 14:19 ` Avi Kivity
2008-09-24 13:11 ` Jan Kiszka
2008-09-24 13:17 ` Gleb Natapov
2008-09-24 13:20 ` Gleb Natapov
2008-09-24 13:39 ` Jan Kiszka
2008-09-24 14:48 ` Gleb Natapov
2008-09-23 9:27 ` Yang, Sheng
2008-09-23 15:15 ` Jan Kiszka
2008-09-25 9:41 ` Jan Kiszka
2008-09-25 10:31 ` Avi Kivity
2008-09-25 14:22 ` Jan Kiszka
2008-09-27 10:57 ` Avi Kivity
2008-09-22 7:59 ` Jan Kiszka [this message]
2008-09-22 8:00 ` [PATCH 4/11] VMX: fix real-mode NMI support Jan Kiszka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=48D7504D.1000600@siemens.com \
--to=jan.kiszka@siemens.com \
--cc=avi@redhat.com \
--cc=gleb@qumranet.com \
--cc=kvm@vger.kernel.org \
--cc=sheng.yang@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).