KVM: PPC: Enable IRQFD support for the XICS interrupt controller
This makes it possible to use IRQFDs on platforms that use the XICS interrupt controller. To do this we implement kvm_irq_map_gsi() and kvm_irq_map_chip_pin() in book3s_xics.c, so as to provide a 1-1 mapping between global interrupt numbers and XICS interrupt source numbers. For now, all interrupts are mapped as "IRQCHIP" interrupts, and no MSI support is provided. This means that kvm_set_irq can now get called with level == 0 or 1 as well as the powerpc-specific values KVM_INTERRUPT_SET, KVM_INTERRUPT_UNSET and KVM_INTERRUPT_SET_LEVEL. We change ics_deliver_irq() to accept all those values, and remove its report_status argument, as it is always false, given that we don't support KVM_IRQ_LINE_STATUS. This also adds support for interrupt ack notifiers to the XICS code so that the IRQFD resampler functionality can be supported. Signed-off-by: Paul Mackerras <paulus@samba.org> Tested-by: Eric Auger <eric.auger@linaro.org> Tested-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
297e21053a
commit
25a2150bee
4 changed files with 55 additions and 9 deletions
|
@ -170,6 +170,8 @@ config KVM_MPIC
|
|||
config KVM_XICS
|
||||
bool "KVM in-kernel XICS emulation"
|
||||
depends on KVM_BOOK3S_64 && !KVM_MPIC
|
||||
select HAVE_KVM_IRQCHIP
|
||||
select HAVE_KVM_IRQFD
|
||||
---help---
|
||||
Include support for the XICS (eXternal Interrupt Controller
|
||||
Specification) interrupt controller architecture used on
|
||||
|
|
|
@ -401,6 +401,11 @@ int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
|
|||
icp->rm_action |= XICS_RM_REJECT;
|
||||
icp->rm_reject = irq;
|
||||
}
|
||||
|
||||
if (!hlist_empty(&vcpu->kvm->irq_ack_notifier_list)) {
|
||||
icp->rm_action |= XICS_RM_NOTIFY_EOI;
|
||||
icp->rm_eoied_irq = irq;
|
||||
}
|
||||
bail:
|
||||
return check_too_hard(xics, icp);
|
||||
}
|
||||
|
|
|
@ -64,8 +64,12 @@
|
|||
static void icp_deliver_irq(struct kvmppc_xics *xics, struct kvmppc_icp *icp,
|
||||
u32 new_irq);
|
||||
|
||||
static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level,
|
||||
bool report_status)
|
||||
/*
|
||||
* Return value ideally indicates how the interrupt was handled, but no
|
||||
* callers look at it (given that we don't implement KVM_IRQ_LINE_STATUS),
|
||||
* so just return 0.
|
||||
*/
|
||||
static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level)
|
||||
{
|
||||
struct ics_irq_state *state;
|
||||
struct kvmppc_ics *ics;
|
||||
|
@ -82,17 +86,14 @@ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level,
|
|||
if (!state->exists)
|
||||
return -EINVAL;
|
||||
|
||||
if (report_status)
|
||||
return state->asserted;
|
||||
|
||||
/*
|
||||
* We set state->asserted locklessly. This should be fine as
|
||||
* we are the only setter, thus concurrent access is undefined
|
||||
* to begin with.
|
||||
*/
|
||||
if (level == KVM_INTERRUPT_SET_LEVEL)
|
||||
if (level == 1 || level == KVM_INTERRUPT_SET_LEVEL)
|
||||
state->asserted = 1;
|
||||
else if (level == KVM_INTERRUPT_UNSET) {
|
||||
else if (level == 0 || level == KVM_INTERRUPT_UNSET) {
|
||||
state->asserted = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level,
|
|||
/* Attempt delivery */
|
||||
icp_deliver_irq(xics, NULL, irq);
|
||||
|
||||
return state->asserted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ics_check_resend(struct kvmppc_xics *xics, struct kvmppc_ics *ics,
|
||||
|
@ -772,6 +773,8 @@ static noinline int kvmppc_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
|
|||
if (state->asserted)
|
||||
icp_deliver_irq(xics, icp, irq);
|
||||
|
||||
kvm_notify_acked_irq(vcpu->kvm, 0, irq);
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -789,6 +792,8 @@ static noinline int kvmppc_xics_rm_complete(struct kvm_vcpu *vcpu, u32 hcall)
|
|||
icp_check_resend(xics, icp);
|
||||
if (icp->rm_action & XICS_RM_REJECT)
|
||||
icp_deliver_irq(xics, icp, icp->rm_reject);
|
||||
if (icp->rm_action & XICS_RM_NOTIFY_EOI)
|
||||
kvm_notify_acked_irq(vcpu->kvm, 0, icp->rm_eoied_irq);
|
||||
|
||||
icp->rm_action = 0;
|
||||
|
||||
|
@ -1170,7 +1175,16 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
|||
{
|
||||
struct kvmppc_xics *xics = kvm->arch.xics;
|
||||
|
||||
return ics_deliver_irq(xics, irq, level, line_status);
|
||||
return ics_deliver_irq(xics, irq, level);
|
||||
}
|
||||
|
||||
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *irq_entry, struct kvm *kvm,
|
||||
int irq_source_id, int level, bool line_status)
|
||||
{
|
||||
if (!level)
|
||||
return -1;
|
||||
return kvm_set_irq(kvm, irq_source_id, irq_entry->gsi,
|
||||
level, line_status);
|
||||
}
|
||||
|
||||
static int xics_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
|
@ -1301,3 +1315,26 @@ void kvmppc_xics_free_icp(struct kvm_vcpu *vcpu)
|
|||
vcpu->arch.icp = NULL;
|
||||
vcpu->arch.irq_type = KVMPPC_IRQ_DEFAULT;
|
||||
}
|
||||
|
||||
static int xics_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
return kvm_set_irq(kvm, irq_source_id, e->gsi, level, line_status);
|
||||
}
|
||||
|
||||
int kvm_irq_map_gsi(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *entries, int gsi)
|
||||
{
|
||||
entries->gsi = gsi;
|
||||
entries->type = KVM_IRQ_ROUTING_IRQCHIP;
|
||||
entries->set = xics_set_irq;
|
||||
entries->irqchip.irqchip = 0;
|
||||
entries->irqchip.pin = gsi;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
||||
{
|
||||
return pin;
|
||||
}
|
||||
|
|
|
@ -71,9 +71,11 @@ struct kvmppc_icp {
|
|||
#define XICS_RM_KICK_VCPU 0x1
|
||||
#define XICS_RM_CHECK_RESEND 0x2
|
||||
#define XICS_RM_REJECT 0x4
|
||||
#define XICS_RM_NOTIFY_EOI 0x8
|
||||
u32 rm_action;
|
||||
struct kvm_vcpu *rm_kick_target;
|
||||
u32 rm_reject;
|
||||
u32 rm_eoied_irq;
|
||||
|
||||
/* Debug stuff for real mode */
|
||||
union kvmppc_icp_state rm_dbgstate;
|
||||
|
|
Loading…
Reference in a new issue