[PATCH 7/9] KVM: PPC: Book3S HV: Add support for real mode ICP in XICS emulation
From: Benjamin Herrenschmidt b...@kernel.crashing.org This adds an implementation of the XICS hypercalls in real mode for HV KVM, which allows us to avoid exiting the guest MMU context on all threads for a variety of operations such as fetching a pending interrupt, EOI of messages, IPIs, etc. Signed-off-by: Benjamin Herrenschmidt b...@kernel.crashing.org Signed-off-by: Paul Mackerras pau...@samba.org --- arch/powerpc/kvm/Makefile |1 + arch/powerpc/kvm/book3s_hv_rm_xics.c| 402 +++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 10 +- arch/powerpc/kvm/book3s_xics.c | 64 - arch/powerpc/kvm/book3s_xics.h | 16 ++ 5 files changed, 475 insertions(+), 18 deletions(-) create mode 100644 arch/powerpc/kvm/book3s_hv_rm_xics.c diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index e2eb04c..895e880 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -77,6 +77,7 @@ kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \ book3s_hv_rm_mmu.o \ book3s_64_vio_hv.o \ book3s_hv_ras.o \ + book3s_hv_rm_xics.o \ book3s_hv_builtin.o kvm-book3s_64-module-objs := \ diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c new file mode 100644 index 000..3605e0c --- /dev/null +++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c @@ -0,0 +1,402 @@ +/* + * Copyright 2012 Michael Ellerman, IBM Corporation. + * Copyright 2012 Benjamin Herrenschmidt, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#include linux/kernel.h +#include linux/kvm_host.h +#include linux/err.h + +#include asm/kvm_book3s.h +#include asm/kvm_ppc.h +#include asm/hvcall.h +#include asm/xics.h +#include asm/debug.h +#include asm/synch.h +#include asm/ppc-opcode.h + +#include book3s_xics.h + +#define DEBUG_PASSUP + +static inline void rm_writeb(unsigned long paddr, u8 val) +{ + __asm__ __volatile__(sync; stbcix %0,0,%1 + : : r (val), r (paddr) : memory); +} + +static void icp_rm_set_vcpu_irq(struct kvm_vcpu *vcpu, struct kvm_vcpu *this_vcpu) +{ + struct kvmppc_icp *this_icp = this_vcpu-arch.icp; + unsigned long xics_phys; + int cpu; + + /* Mark the target VCPU as having an interrupt pending */ + vcpu-stat.queue_intr++; + set_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, vcpu-arch.pending_exceptions); + + /* Kick self ? Just set MER and return */ + if (vcpu == this_vcpu) { + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_MER); + return; + } + + /* Check if the core is loaded, if not, too hard */ + cpu = vcpu-cpu; + if (cpu 0 || cpu = nr_cpu_ids) { + this_icp-rm_action |= XICS_RM_KICK_VCPU; + this_icp-rm_kick_target = vcpu; + return; + } + /* In SMT cpu will always point to thread 0, we adjust it */ + cpu += vcpu-arch.ptid; + + /* Not too hard, then poke the target */ + xics_phys = paca[cpu].kvm_hstate.xics_phys; + rm_writeb(xics_phys + XICS_MFRR, IPI_PRIORITY); +} + +static void icp_rm_clr_vcpu_irq(struct kvm_vcpu *vcpu) +{ + /* Note: Only called on self ! */ + clear_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, vcpu-arch.pending_exceptions); + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) ~LPCR_MER); +} + +static inline bool icp_rm_try_update(struct kvmppc_icp *icp, +union kvmppc_icp_state old, +union kvmppc_icp_state new) +{ + struct kvm_vcpu *this_vcpu = local_paca-kvm_hstate.kvm_vcpu; + bool success; + + /* Calculate new output value */ + new.out_ee = (new.xisr (new.pending_pri new.cppr)); + + /* Attempt atomic update */ + success = cmpxchg64(icp-state.raw, old.raw, new.raw) == old.raw; + if (!success) + goto bail; + + /* +* Check for output state update +* +* Note that this is racy since another processor could be updating +* the state already. This is why we never clear the interrupt output +* here, we only ever set it. The clear only happens prior to doing +* an update and only by the processor itself. Currently we do it +* in Accept (H_XIRR) and Up_Cppr (H_XPPR). +* +* We also do not try to figure out whether the EE state has changed, +* we unconditionally set it if the new state calls for it. The reason +* for that is that we opportunistically remove the pending interrupt +* flag when raising CPPR, so we need to set it back here if an +* interrupt is still pending. +*/ + if (new.out_ee) + icp_rm_set_vcpu_irq(icp-vcpu, this_vcpu); + + /* Expose the state
[PATCH 7/9] KVM: PPC: Book3S HV: Add support for real mode ICP in XICS emulation
From: Benjamin Herrenschmidt b...@kernel.crashing.org This adds an implementation of the XICS hypercalls in real mode for HV KVM, which allows us to avoid exiting the guest MMU context on all threads for a variety of operations such as fetching a pending interrupt, EOI of messages, IPIs, etc. Signed-off-by: Benjamin Herrenschmidt b...@kernel.crashing.org Signed-off-by: Paul Mackerras pau...@samba.org --- arch/powerpc/kvm/Makefile |1 + arch/powerpc/kvm/book3s_hv_rm_xics.c| 402 +++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 10 +- arch/powerpc/kvm/book3s_xics.c | 64 - arch/powerpc/kvm/book3s_xics.h | 16 ++ 5 files changed, 475 insertions(+), 18 deletions(-) create mode 100644 arch/powerpc/kvm/book3s_hv_rm_xics.c diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index e2eb04c..895e880 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -77,6 +77,7 @@ kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \ book3s_hv_rm_mmu.o \ book3s_64_vio_hv.o \ book3s_hv_ras.o \ + book3s_hv_rm_xics.o \ book3s_hv_builtin.o kvm-book3s_64-module-objs := \ diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c new file mode 100644 index 000..3605e0c --- /dev/null +++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c @@ -0,0 +1,402 @@ +/* + * Copyright 2012 Michael Ellerman, IBM Corporation. + * Copyright 2012 Benjamin Herrenschmidt, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#include linux/kernel.h +#include linux/kvm_host.h +#include linux/err.h + +#include asm/kvm_book3s.h +#include asm/kvm_ppc.h +#include asm/hvcall.h +#include asm/xics.h +#include asm/debug.h +#include asm/synch.h +#include asm/ppc-opcode.h + +#include book3s_xics.h + +#define DEBUG_PASSUP + +static inline void rm_writeb(unsigned long paddr, u8 val) +{ + __asm__ __volatile__(sync; stbcix %0,0,%1 + : : r (val), r (paddr) : memory); +} + +static void icp_rm_set_vcpu_irq(struct kvm_vcpu *vcpu, struct kvm_vcpu *this_vcpu) +{ + struct kvmppc_icp *this_icp = this_vcpu-arch.icp; + unsigned long xics_phys; + int cpu; + + /* Mark the target VCPU as having an interrupt pending */ + vcpu-stat.queue_intr++; + set_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, vcpu-arch.pending_exceptions); + + /* Kick self ? Just set MER and return */ + if (vcpu == this_vcpu) { + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_MER); + return; + } + + /* Check if the core is loaded, if not, too hard */ + cpu = vcpu-cpu; + if (cpu 0 || cpu = nr_cpu_ids) { + this_icp-rm_action |= XICS_RM_KICK_VCPU; + this_icp-rm_kick_target = vcpu; + return; + } + /* In SMT cpu will always point to thread 0, we adjust it */ + cpu += vcpu-arch.ptid; + + /* Not too hard, then poke the target */ + xics_phys = paca[cpu].kvm_hstate.xics_phys; + rm_writeb(xics_phys + XICS_MFRR, IPI_PRIORITY); +} + +static void icp_rm_clr_vcpu_irq(struct kvm_vcpu *vcpu) +{ + /* Note: Only called on self ! */ + clear_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, vcpu-arch.pending_exceptions); + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) ~LPCR_MER); +} + +static inline bool icp_rm_try_update(struct kvmppc_icp *icp, +union kvmppc_icp_state old, +union kvmppc_icp_state new) +{ + struct kvm_vcpu *this_vcpu = local_paca-kvm_hstate.kvm_vcpu; + bool success; + + /* Calculate new output value */ + new.out_ee = (new.xisr (new.pending_pri new.cppr)); + + /* Attempt atomic update */ + success = cmpxchg64(icp-state.raw, old.raw, new.raw) == old.raw; + if (!success) + goto bail; + + /* +* Check for output state update +* +* Note that this is racy since another processor could be updating +* the state already. This is why we never clear the interrupt output +* here, we only ever set it. The clear only happens prior to doing +* an update and only by the processor itself. Currently we do it +* in Accept (H_XIRR) and Up_Cppr (H_XPPR). +* +* We also do not try to figure out whether the EE state has changed, +* we unconditionally set it if the new state calls for it. The reason +* for that is that we opportunistically remove the pending interrupt +* flag when raising CPPR, so we need to set it back here if an +* interrupt is still pending. +*/ + if (new.out_ee) + icp_rm_set_vcpu_irq(icp-vcpu, this_vcpu); + + /* Expose the state