Skip to contentSkip to conversation

Add support for handling IRQs in userspace

PATCH New Pavel Fedin 1 participant 4 messages Dec 1, 2015

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover Add support for handling IRQs in userspace

This patch series introduces ability to handle IRQs in userspace. This is currently necessary for ARM KVM in order to be able to use virtual CP15 timer without in-kernel irqchip. This allows to use KVM on machines with either broken vGIC or custom interrupt controller, like Raspberry Pi 2.

The API is designed to be as much architecture-agnostic is possible. Currently it actually supports only a single IRQ, but it can be easily extended to accomodate more.

Pavel Fedin (3):
KVM: Introduce KVM_EXIT_IRQ
KVM: Documentation: Document KVM_EXIT_IRQ
KVM: arm/arm64: Decouple virtual timer from vGIC

 Documentation/virtual/kvm/api.txt | 14 ++++++++++++++
 arch/arm/kvm/arm.c                | 14 +++++++-------
 include/linux/kvm_host.h          |  7 +++++++
 include/uapi/linux/kvm.h          |  3 +++
 virt/kvm/arm/arch_timer.c         | 23 +++++++++++++----------
 5 files changed, 44 insertions(+), 17 deletions(-)
Signature · 1 line
2.4.4
#1 lore raw

PATCH 1/3 KVM: Introduce KVM_EXIT_IRQ

This exit code means that this vCPU wants to inject an interrupt using userspace-emulated controller.

IRQs are signalled by adding pending interrupt descriptors to vcpu structure. For simplicity, we currently reserve only one pointer for a single interrupt, which will be used by ARM virtual timer code. This can be extended in the future if necessary.

The interface is designed to be as much arch-agnostic as possible. Therefore, it has IRQ number and level as parameters (encoded in struct kvm_irq_level).

---
 arch/arm/kvm/arm.c       | 6 ++++++
 include/linux/kvm_host.h | 7 +++++++
 include/uapi/linux/kvm.h | 3 +++
 3 files changed, 16 insertions(+)
arch/arm/kvm/arm.c +6 -0
index 66f90c1..6392a5b 100644
@@ -585,6 +585,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		if (signal_pending(current)) {
 			ret = -EINTR;
 			run->exit_reason = KVM_EXIT_INTR;
+		} else if (vcpu->irq) {
+			ret = 0;
+			run->exit_reason = KVM_EXIT_IRQ;
+			run->irq.irq = vcpu->irq->irq;
+			run->irq.level = vcpu->irq->level;
+			vcpu->irq = NULL;
 		}
 
 		if (ret <= 0 || need_new_vmid_gen(vcpu->kvm) ||
include/linux/kvm_host.h +7 -0
index c923350..93f59c5 100644
@@ -281,6 +281,13 @@ struct kvm_vcpu {
 	} spin_loop;
 #endif
 	bool preempted;
+
+	/*
+	 * IRQ pending to the userspace on this CPU.
+	 * Currently we support only one slot, used only by ARM architecture.
+	 */
+	const struct kvm_irq_level *irq;
+
 	struct kvm_vcpu_arch arch;
 };
 
include/uapi/linux/kvm.h +3 -0
index 03f3618..a717a9b 100644
@@ -184,6 +184,7 @@ struct kvm_s390_skeys {
 #define KVM_EXIT_SYSTEM_EVENT     24
 #define KVM_EXIT_S390_STSI        25
 #define KVM_EXIT_IOAPIC_EOI       26
+#define KVM_EXIT_IRQ              27
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -338,6 +339,8 @@ struct kvm_run {
 		struct {
 			__u8 vector;
 		} eoi;
+		/* KVM_EXIT_IRQ */
+		struct kvm_irq_level irq;
 		/* Fix the size of the union. */
 		char padding[256];
 	};
Signature · 1 line
2.4.4
#2 lore raw

PATCH 2/3 KVM: Documentation: Document KVM_EXIT_IRQ

In reply to Pavel Fedin

Add documentation for the new exit code.

---
 Documentation/virtual/kvm/api.txt | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
Documentation/virtual/kvm/api.txt +14 -0
index 092ee9f..d8aae4c 100644
@@ -3331,6 +3331,20 @@ the userspace IOAPIC should process the EOI and retrigger the interrupt if
 it is still asserted.  Vector is the LAPIC interrupt vector for which the
 EOI was received.
 
+		/* KVM_EXIT_IRQ */
+		struct kvm_irq_level irq;
+
+Indicates that an interrupt happens, to be processed by irqchip implemented in
+userspace. irq.irq specifies the raw IRQ number, and irq.status is to be
+interpreted according to interrupt type:
+ For level-triggered interrupts irq.status is set to new level of the line, and
+  the exit happens upon level change.
+ For edge-triggered interrupts irq.status is set to active level of the line
+  (low or high), and the exit happens when the line is pulsed.
+
+CPU-private interrupts (like per-CPU timers) belong to the vCPU where the exit
+happened.
+
 		/* Fix the size of the union. */
 		char padding[256];
 	};
Signature · 1 line
2.4.4
#3 lore raw

PATCH 3/3 KVM: arm/arm64: Decouple virtual timer from vGIC

In reply to Pavel Fedin

Remove dependency on vgic_initialized() and use the newly introduced infrastructure to send interrupts via the userspace if vGIC is not being used.

---
 arch/arm/kvm/arm.c        |  8 +-------
 virt/kvm/arm/arch_timer.c | 23 +++++++++++++----------
 2 files changed, 14 insertions(+), 17 deletions(-)
arch/arm/kvm/arm.c +1 -7
index 6392a5b..e729068 100644
@@ -468,13 +468,7 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 			return ret;
 	}
 
-	/*
-	 * Enable the arch timers only if we have an in-kernel VGIC
-	 * and it has been properly initialized, since we cannot handle
-	 * interrupts from the virtual timer with a userspace gic.
-	 */
-	if (irqchip_in_kernel(kvm) && vgic_initialized(kvm))
-		kvm_timer_enable(kvm);
+	kvm_timer_enable(kvm);
 
 	return 0;
 }
virt/kvm/arm/arch_timer.c +13 -10
index 69bca18..90c91b0 100644
@@ -128,15 +128,17 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
 	int ret;
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 
-	BUG_ON(!vgic_initialized(vcpu->kvm));
-
 	timer->irq.level = new_level;
 	trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->map->virt_irq,
 				   timer->irq.level);
-	ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
-					 timer->map,
-					 timer->irq.level);
-	WARN_ON(ret);
+	if (irqchip_in_kernel(vcpu->kvm)) {
+		ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
+						 timer->map,
+						 timer->irq.level);
+		WARN_ON(ret);
+	} else {
+		vcpu->irq = &timer->irq;
+	}
 }
 
 /*
@@ -149,12 +151,12 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu)
 
 	/*
 	 * If userspace modified the timer registers via SET_ONE_REG before
-	 * the vgic was initialized, we mustn't set the timer->irq.level value
+	 * the timer was initialized, we mustn't set the timer->irq.level value
 	 * because the guest would never see the interrupt.  Instead wait
 	 * until we call this function from kvm_timer_flush_hwstate.
 	 */
-	if (!vgic_initialized(vcpu->kvm))
-	    return;
+	if (!vcpu->kvm->arch.timer.enabled)
+		return;
 
 	if (kvm_timer_should_fire(vcpu) != timer->irq.level)
 		kvm_timer_update_irq(vcpu, !timer->irq.level);
@@ -237,7 +239,8 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
 	* to ensure that hardware interrupts from the timer triggers a guest
 	* exit.
 	*/
-	if (timer->irq.level || kvm_vgic_map_is_active(vcpu, timer->map))
+	if (timer->irq.level || (irqchip_in_kernel(vcpu->kvm) &&
+				 kvm_vgic_map_is_active(vcpu, timer->map)))
 		phys_active = true;
 	else
 		phys_active = false;
Signature · 1 line
2.4.4