Add support for handling IRQs in userspace
Subsystems (4)
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
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
@@ -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
@@ -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
@@ -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
PATCH 2/3 KVM: Documentation: Document KVM_EXIT_IRQ
In reply to Pavel FedinAdd documentation for the new exit code.
--- Documentation/virtual/kvm/api.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
Documentation/virtual/kvm/api.txt +14 -0
@@ -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
PATCH 3/3 KVM: arm/arm64: Decouple virtual timer from vGIC
In reply to Pavel FedinRemove 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
@@ -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
@@ -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