Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25a2150b authored by Paul Mackerras's avatar Paul Mackerras Committed by Paolo Bonzini
Browse files

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: default avatarPaul Mackerras <paulus@samba.org>
Tested-by: default avatarEric Auger <eric.auger@linaro.org>
Tested-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 297e2105
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
@@ -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);
}
+46 −9
Original line number Diff line number Diff line
@@ -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;
}
+2 −0
Original line number Diff line number Diff line
@@ -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;