summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/kvm/Kconfig1
-rw-r--r--arch/powerpc/kvm/book3s_hv.c39
-rw-r--r--arch/powerpc/kvm/booke.c1
-rw-r--r--arch/powerpc/kvm/powerpc.c37
4 files changed, 55 insertions, 23 deletions
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
index 9a0d1c1aca6c..b6bc2fc86dca 100644
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -22,6 +22,7 @@ config KVM
select KVM_COMMON
select KVM_VFIO
select HAVE_KVM_IRQ_BYPASS
+ select VIRT_XFER_TO_GUEST_WORK
config KVM_BOOK3S_HANDLER
bool
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 61dbeea317f3..3cfe9a7be9c6 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3853,7 +3853,8 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc)
*/
local_irq_disable();
hard_irq_disable();
- if (lazy_irq_pending() || need_resched() ||
+ xfer_to_guest_mode_prepare();
+ if (lazy_irq_pending() || xfer_to_guest_mode_work_pending() ||
recheck_signals_and_mmu(&core_info)) {
local_irq_enable();
vc->vcore_state = VCORE_INACTIVE;
@@ -4824,10 +4825,16 @@ static int kvmppc_run_vcpu(struct kvm_vcpu *vcpu)
vc->runner = vcpu;
if (n_ceded == vc->n_runnable) {
kvmppc_vcore_blocked(vc);
- } else if (need_resched()) {
+ } else if (__xfer_to_guest_mode_work_pending()) {
kvmppc_vcore_preempt(vc);
- /* Let something else run */
- cond_resched_lock(&vc->lock);
+ /*
+ * Let something else run. The raw helper is used as
+ * signal exits are accounted by this path already;
+ * it may schedule(), so drop the vcore lock.
+ */
+ spin_unlock(&vc->lock);
+ xfer_to_guest_mode_handle_work();
+ spin_lock(&vc->lock);
if (vc->vcore_state == VCORE_PREEMPT)
kvmppc_vcore_end_preempt(vc);
} else {
@@ -4895,12 +4902,16 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
run->exit_reason = KVM_EXIT_FAIL_ENTRY;
run->fail_entry.hardware_entry_failure_reason = 0;
vcpu->arch.ret = r;
- return r;
+ goto done;
}
}
- if (need_resched())
- cond_resched();
+ r = kvm_xfer_to_guest_mode_handle_work(vcpu);
+ if (r) {
+ /* -EINTR: signal pending, exit to userspace (KVM_EXIT_INTR) */
+ vcpu->arch.ret = r;
+ goto done;
+ }
kvmppc_update_vpas(vcpu);
@@ -4914,9 +4925,13 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
vcpu->arch.state = KVMPPC_VCPU_RUNNABLE;
- if (signal_pending(current))
- goto sigpend;
- if (need_resched() || !kvm->arch.mmu_ready)
+ xfer_to_guest_mode_prepare();
+
+ /*
+ * IRQs are disabled here, so on pending work bail to the outer loop,
+ * which handles it via kvm_xfer_to_guest_mode_handle_work() above.
+ */
+ if (xfer_to_guest_mode_work_pending() || !kvm->arch.mmu_ready)
goto out;
vcpu->cpu = pcpu;
@@ -5068,10 +5083,6 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
return vcpu->arch.ret;
- sigpend:
- vcpu->stat.signal_exits++;
- run->exit_reason = KVM_EXIT_INTR;
- vcpu->arch.ret = -EINTR;
out:
vcpu->cpu = -1;
vcpu->arch.thread_cpu = -1;
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index f3ddb24ece74..5fba199dfdd6 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -722,6 +722,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
if (vcpu->arch.shared->msr & MSR_WE) {
local_irq_enable();
kvm_vcpu_halt(vcpu);
+ local_irq_disable();
hard_irq_disable();
kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 00302399fc37..be5e48ae0c6c 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -81,23 +81,41 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
int r;
WARN_ON(irqs_disabled());
+ /*
+ * local_irq_disable() first: on 32-bit, hard_irq_disable() alone is a
+ * raw MSR[EE] clear that bypasses the lockdep/irq-tracing state, and
+ * the xfer_to_guest_mode helpers assert IRQs are seen as disabled.
+ */
+ local_irq_disable();
hard_irq_disable();
while (true) {
- if (need_resched()) {
+ xfer_to_guest_mode_prepare();
+
+ if (xfer_to_guest_mode_work_pending()) {
+ /*
+ * The helper must run with IRQs enabled and may
+ * schedule(). On a pending signal it returns -EINTR
+ * with run->exit_reason and vcpu->stat.signal_exits
+ * already set, so just return to userspace.
+ */
local_irq_enable();
- cond_resched();
+ r = kvm_xfer_to_guest_mode_handle_work(vcpu);
+ local_irq_disable();
hard_irq_disable();
+ if (r) {
+ /*
+ * The generic helper does not set the exit
+ * type; record it for the E500
+ * CONFIG_KVM_EXIT_TIMING histogram (a no-op
+ * otherwise).
+ */
+ kvmppc_set_exit_type(vcpu, SIGNAL_EXITS);
+ break;
+ }
continue;
}
- if (signal_pending(current)) {
- kvmppc_account_exit(vcpu, SIGNAL_EXITS);
- vcpu->run->exit_reason = KVM_EXIT_INTR;
- r = -EINTR;
- break;
- }
-
vcpu->mode = IN_GUEST_MODE;
/*
@@ -116,6 +134,7 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
local_irq_enable();
trace_kvm_check_requests(vcpu);
r = kvmppc_core_check_requests(vcpu);
+ local_irq_disable();
hard_irq_disable();
if (r > 0)
continue;