From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:50
This series illustrates an alternative approach to Eric Auger's direct EOI
setup patches [1] in terms of the KVM VGIC support.
The idea is to maintain existing semantics for the VGIC for mapped
level-triggered IRQs and think support for the timer into it.
Patch 1 is necessary to align the timer and VFIO ways of signaling the
VGIC. Patch 2 is stolen from Eric's series and is necessary for these
patches to compile as well. Patch 3 includes the core support for
mapped level-triggered interrupts. Patch 4 handles guest MMIO acess to
the virtual distributor. Patch 5 moves some code around for patch 6.
Patch 6 implements an optimization for the timer. The last two patches
could be deferred until the timer optimization series.
Based on v4.13-rc7.
Thanks,
-Christoffer
Changes since v1:
- Added necessary changes to the timer (Patch 1)
- Added handling of guest MMIO accesses to the virtual distributor
(Patch 4)
- Addressed Marc's comments from the initial RFC (mostly renames)
Christoffer Dall (5):
KVM: arm/arm64: Don't cache the timer IRQ level
KVM: arm/arm64: vgic: Support level-triggered mapped interrupts
KVM: arm/arm64: Support VGIC dist pend/active changes for mapped IRQs
KVM: arm/arm64: Rearrange kvm_vgic_[un]map_phys code in vgic.c
KVM: arm/arm64: Provide a vgic interrupt line level sample function
Eric Auger (1):
KVM: arm/arm64: vgic: restructure kvm_vgic_(un)map_phys_irq
include/kvm/arm_vgic.h | 19 +++++++++--
virt/kvm/arm/arch_timer.c | 52 +++++++++++++-----------------
virt/kvm/arm/vgic/vgic-mmio.c | 27 ++++++++++++++++
virt/kvm/arm/vgic/vgic-v2.c | 29 +++++++++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 29 +++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 75 ++++++++++++++++++++++++++++++++++++-------
virt/kvm/arm/vgic/vgic.h | 8 +++++
7 files changed, 196 insertions(+), 43 deletions(-)
--
2.9.0
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:51
The timer was modeled after a strict idea of modelling an interrupt line
level in software, meaning that only transitions in the level needed to
be reported to the VGIC. This works well for the timer, because the
arch timer code is in complete control of the device and can track the
transitions of the line.
However, as we are about to support using the HW bit in the VGIC not
just for the timer, but also for VFIO which cannot track transitions of
the interrupt line, we have to decide on an interface for level
triggered mapped interrupts to the GIC, which both the timer and VFIO
can use.
VFIO only sees an asserting transition of the physical interrupt line,
and tells the VGIC when that happens. That means that part of the
interrupt flow is offloaded to the hardware.
To use the same interface for VFIO devices and the timer, we therefore
have to change the timer (we cannot change VFIO because it doesn't know
the details of the device it is assigning to a VM).
Luckily, changing the timer is simple, we just need to stop 'caching'
the line level, but instead let the VGIC know the state of the timer on
every entry to the guest, and the VGIC can ignore notifications using
its validate mechanism.
Signed-off-by: Christoffer Dall <redacted>
---
virt/kvm/arm/arch_timer.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:52
From: Eric Auger <eric.auger@redhat.com>
We want to reuse the core of the map/unmap functions for IRQ forwarding.
Let's move the computation of the hwirq in kvm_vgic_map_phys_irq and
pass the linux IRQ as parameter.
The host_irq is added to struct vgic_irq because it is needed in later
patches which manipulate the physical GIC state to support forwarded
IRQs.
We introduce kvm_vgic_map/unmap_irq which take a struct vgic_irq handle
as a parameter.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Christoffer Dall <redacted>
Acked-by: Marc Zyngier <redacted>
---
include/kvm/arm_vgic.h | 8 ++++---
virt/kvm/arm/arch_timer.c | 24 +------------------
virt/kvm/arm/vgic/vgic.c | 60 +++++++++++++++++++++++++++++++++++------------
3 files changed, 51 insertions(+), 41 deletions(-)
@@ -403,38 +405,66 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,return0;}-intkvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,u32virt_irq,u32phys_irq)+/* @irq->irq_lock must be held */+staticintkvm_vgic_map_irq(structkvm_vcpu*vcpu,structvgic_irq*irq,+unsignedinthost_irq){-structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,virt_irq);+structirq_desc*desc;+structirq_data*data;-BUG_ON(!irq);--spin_lock(&irq->irq_lock);+/*+*FindthephysicalIRQnumbercorrespondingto@host_irq+*/+desc=irq_to_desc(host_irq);+if(!desc){+kvm_err("%s: no interrupt descriptor\n",__func__);+return-EINVAL;+}+data=irq_desc_get_irq_data(desc);+while(data->parent_data)+data=data->parent_data;irq->hw=true;-irq->hwintid=phys_irq;+irq->host_irq=host_irq;+irq->hwintid=data->hwirq;+return0;+}++/* @irq->irq_lock must be held */+staticinlinevoidkvm_vgic_unmap_irq(structvgic_irq*irq)+{+irq->hw=false;+irq->hwintid=0;+}++intkvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,unsignedinthost_irq,+u32vintid)+{+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,vintid);+intret;+BUG_ON(!irq);++spin_lock(&irq->irq_lock);+ret=kvm_vgic_map_irq(vcpu,irq,host_irq);spin_unlock(&irq->irq_lock);vgic_put_irq(vcpu->kvm,irq);-return0;+returnret;}-intkvm_vgic_unmap_phys_irq(structkvm_vcpu*vcpu,unsignedintvirt_irq)+intkvm_vgic_unmap_phys_irq(structkvm_vcpu*vcpu,unsignedintvintid){structvgic_irq*irq;if(!vgic_initialized(vcpu->kvm))return-EAGAIN;-irq=vgic_get_irq(vcpu->kvm,vcpu,virt_irq);+irq=vgic_get_irq(vcpu->kvm,vcpu,vintid);BUG_ON(!irq);spin_lock(&irq->irq_lock);--irq->hw=false;-irq->hwintid=0;-+kvm_vgic_unmap_irq(irq);spin_unlock(&irq->irq_lock);vgic_put_irq(vcpu->kvm,irq);
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:53
Level-triggered mapped IRQs are special because we only observe rising
edges as input to the VGIC, and we don't set the EOI flag and therefore
are not told when the level goes down, so that we can re-queue a new
interrupt when the level goes up.
One way to solve this problem is to side-step the logic of the VGIC and
special case the validation in the injection path, but it has the
unfortunate drawback of having to peak into the physical GIC state
whenever we want to know if the interrupt is pending on the virtual
distributor.
Instead, we can maintain the current semantics of a level triggered
interrupt by sort of treating it as an edge-triggered interrupt,
following from the fact that we only observe an asserting edge. This
requires us to be a bit careful when populating the LRs and when folding
the state back in though:
* We lower the line level when populating the LR, so that when
subsequently observing an asserting edge, the VGIC will do the right
thing.
* If the guest never acked the interrupt while running (for example if
it had masked interrupts at the CPU level while running), we have
to preserve the pending state of the LR and move it back to the
line_level field of the struct irq when folding LR state.
If the guest never acked the interrupt while running, but changed the
device state and lowered the line (again with interrupts masked) then
we need to observe this change in the line_level.
Both of the above situations are solved by sampling the physical line
and set the line level when folding the LR back.
* Finally, if the guest never acked the interrupt while running and
sampling the line reveals that the device state has changed and the
line has been lowered, we must clear the physical active state, since
we will otherwise never be told when the interrupt becomes asserted
again.
This has the added benefit of making the timer optimization patches
(https://lists.cs.columbia.edu/pipermail/kvmarm/2017-July/026343.html) a
bit simpler, because the timer code doesn't have to clear the active
state on the sync anymore. It also potentially improves the performance
of the timer implementation because the GIC knows the state or the LR
and only needs to clear the
active state when the pending bit in the LR is still set, where the
timer has to always clear it when returning from running the guest with
an injected timer interrupt.
Signed-off-by: Christoffer Dall <redacted>
Reviewed-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic-v2.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 23 +++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 7 +++++++
4 files changed, 88 insertions(+)
@@ -161,6 +181,15 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)val|=GICH_LR_EOI;}+/*+*Level-triggeredmappedIRQsarespecialbecauseweonlyobserve+*risingedgesasinputtotheVGIC.Wethereforelowertheline+*levelhere,sothatwecantakenewvirtualIRQs.See+*vgic_v2_fold_lr_stateformoreinfo.+*/+if(vgic_irq_is_mapped_level(irq)&&(val&GICH_LR_PENDING_BIT))+irq->line_level=false;+/* The GICv2 LR only holds five bits of priority. */val|=(irq->priority>>3)<<GICH_LR_PRIORITY_SHIFT;
@@ -140,6 +140,29 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)kfree(irq);}+/* Get the input level of a mapped IRQ directly from the physical GIC */+boolvgic_get_phys_line_level(structvgic_irq*irq)+{+boolline_level;++BUG_ON(!irq->hw);++WARN_ON(irq_get_irqchip_state(irq->host_irq,+IRQCHIP_STATE_PENDING,+&line_level));+returnline_level;+}++/* Set/Clear the physical active state */+voidvgic_irq_set_phys_active(structvgic_irq*irq,boolactive)+{++BUG_ON(!irq->hw);+WARN_ON(irq_set_irqchip_state(irq->host_irq,+IRQCHIP_STATE_ACTIVE,+active));+}+/***kvm_vgic_target_oracle-computethetargetvcpuforanirq*
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:54
For mapped IRQs (with the HW bit set in the LR) we have to follow some
rules of the architecture. One of these rules is that VM must not be
allowed to deactivate a virtual interrupt with the HW bit set unless the
physical interrupt is also active.
This works fine when injecting mapped interrupts, because we leave it up
to the injector to either set EOImode==1 or manually set the active
state of the physical interrupt.
However, the guest can set virtual interrupt to be pending or active by
writing to the virtual distributor, which could lead to deactivating a
virtual interrupt with the HW bit set without the physical interrupt
being active.
We could set the physical interrupt to active whenever we are about to
eneter the VM with a HW interrupt either pending or active, but that
would be really slow, especially on GICv2. So we take the long way
around and do the hard work when needed, which is expected to be
extremely rare.
When the VM sets the pending state for a HW interrupt on the virtual
distributor we set the active state on the physical distributor, because
the virtual interrupt can become active and then the guest can
deactivate it.
When the VM clears the pending state we also clear it on the physical
side, because the injector might otherwise raise the interrupt.
Signed-off-by: Christoffer Dall <redacted>
---
virt/kvm/arm/vgic/vgic-mmio.c | 27 +++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 7 +++++++
virt/kvm/arm/vgic/vgic.h | 1 +
3 files changed, 35 insertions(+)
@@ -140,6 +140,13 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)kfree(irq);}+voidvgic_irq_set_phys_pending(structvgic_irq*irq,boolpending)+{+WARN_ON(irq_set_irqchip_state(irq->host_irq,+IRQCHIP_STATE_PENDING,+pending));+}+/* Get the input level of a mapped IRQ directly from the physical GIC */boolvgic_get_phys_line_level(structvgic_irq*irq){
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:55
The small indirection of a static function made the locking very obvious
but becomes pretty ugly as we start passing function pointer around.
Let's inline these two functions first to make the following patch more
readable.
Signed-off-by: Christoffer Dall <redacted>
Acked-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)
@@ -435,12 +435,17 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,return0;}-/* @irq->irq_lock must be held */-staticintkvm_vgic_map_irq(structkvm_vcpu*vcpu,structvgic_irq*irq,-unsignedinthost_irq)+intkvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,unsignedinthost_irq,+u32vintid){+structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,vintid);structirq_desc*desc;structirq_data*data;+intret=0;++BUG_ON(!irq);++spin_lock(&irq->irq_lock);/**FindthephysicalIRQnumbercorrespondingto@host_irq
@@ -448,7 +453,8 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,desc=irq_to_desc(host_irq);if(!desc){kvm_err("%s: no interrupt descriptor\n",__func__);-return-EINVAL;+ret=-EINVAL;+gotoout;}data=irq_desc_get_irq_data(desc);while(data->parent_data)
@@ -457,29 +463,10 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,irq->hw=true;irq->host_irq=host_irq;irq->hwintid=data->hwirq;-return0;-}--/* @irq->irq_lock must be held */-staticinlinevoidkvm_vgic_unmap_irq(structvgic_irq*irq)-{-irq->hw=false;-irq->hwintid=0;-}--intkvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,unsignedinthost_irq,-u32vintid)-{-structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,vintid);-intret;-BUG_ON(!irq);--spin_lock(&irq->irq_lock);-ret=kvm_vgic_map_irq(vcpu,irq,host_irq);+out:spin_unlock(&irq->irq_lock);vgic_put_irq(vcpu->kvm,irq);-returnret;}
@@ -494,7 +481,8 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid)BUG_ON(!irq);spin_lock(&irq->irq_lock);-kvm_vgic_unmap_irq(irq);+irq->hw=false;+irq->hwintid=0;spin_unlock(&irq->irq_lock);vgic_put_irq(vcpu->kvm,irq);
From: Christoffer Dall <hidden> Date: 2017-09-04 10:24:56
The GIC sometimes need to sample the physical line of a mapped
interrupt. As we know this to be notoriously slow, provide a callback
function for devices (such as the timer) which can do this much faster
than talking to the distributor, for example by comparing a few
in-memory values. Fall back to the good old method of poking the
physical GIC if no callback is provided.
Signed-off-by: Christoffer Dall <redacted>
Reviewed-by: Marc Zyngier <redacted>
---
include/kvm/arm_vgic.h | 13 ++++++++++++-
virt/kvm/arm/arch_timer.c | 16 +++++++++++++++-
virt/kvm/arm/vgic/vgic.c | 7 ++++++-
3 files changed, 33 insertions(+), 3 deletions(-)
@@ -645,6 +645,19 @@ static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu)returntrue;}+staticbooltimer_get_input_level(intvintid)+{+structkvm_vcpu*vcpu=kvm_arm_get_running_vcpu();+structarch_timer_context*timer;++if(vintid==vcpu_vtimer(vcpu)->irq.irq)+timer=vcpu_vtimer(vcpu);+else+BUG();/* We only map the vtimer so far */++returnkvm_timer_should_fire(timer);+}+intkvm_timer_enable(structkvm_vcpu*vcpu){structarch_timer_cpu*timer=&vcpu->arch.timer_cpu;
@@ -666,7 +679,8 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)return-EINVAL;}-ret=kvm_vgic_map_phys_irq(vcpu,host_vtimer_irq,vtimer->irq.irq);+ret=kvm_vgic_map_phys_irq(vcpu,host_vtimer_irq,vtimer->irq.irq,+timer_get_input_level);if(ret)returnret;
@@ -436,7 +439,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,}intkvm_vgic_map_phys_irq(structkvm_vcpu*vcpu,unsignedinthost_irq,-u32vintid)+u32vintid,bool(*get_input_level)(intvindid)){structvgic_irq*irq=vgic_get_irq(vcpu->kvm,vcpu,vintid);structirq_desc*desc;
@@ -463,6 +466,7 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,irq->hw=true;irq->host_irq=host_irq;irq->hwintid=data->hwirq;+irq->get_input_level=get_input_level;out:spin_unlock(&irq->irq_lock);
@@ -483,6 +487,7 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid)spin_lock(&irq->irq_lock);irq->hw=false;irq->hwintid=0;+irq->get_input_level=NULL;spin_unlock(&irq->irq_lock);vgic_put_irq(vcpu->kvm,irq);
Hi Christoffer,
On 04/09/2017 12:24, Christoffer Dall wrote:
quoted hunk
Level-triggered mapped IRQs are special because we only observe rising
edges as input to the VGIC, and we don't set the EOI flag and therefore
are not told when the level goes down, so that we can re-queue a new
interrupt when the level goes up.
One way to solve this problem is to side-step the logic of the VGIC and
special case the validation in the injection path, but it has the
unfortunate drawback of having to peak into the physical GIC state
whenever we want to know if the interrupt is pending on the virtual
distributor.
Instead, we can maintain the current semantics of a level triggered
interrupt by sort of treating it as an edge-triggered interrupt,
following from the fact that we only observe an asserting edge. This
requires us to be a bit careful when populating the LRs and when folding
the state back in though:
* We lower the line level when populating the LR, so that when
subsequently observing an asserting edge, the VGIC will do the right
thing.
* If the guest never acked the interrupt while running (for example if
it had masked interrupts at the CPU level while running), we have
to preserve the pending state of the LR and move it back to the
line_level field of the struct irq when folding LR state.
If the guest never acked the interrupt while running, but changed the
device state and lowered the line (again with interrupts masked) then
we need to observe this change in the line_level.
Both of the above situations are solved by sampling the physical line
and set the line level when folding the LR back.
* Finally, if the guest never acked the interrupt while running and
sampling the line reveals that the device state has changed and the
line has been lowered, we must clear the physical active state, since
we will otherwise never be told when the interrupt becomes asserted
again.
This has the added benefit of making the timer optimization patches
(https://lists.cs.columbia.edu/pipermail/kvmarm/2017-July/026343.html) a
bit simpler, because the timer code doesn't have to clear the active
state on the sync anymore. It also potentially improves the performance
of the timer implementation because the GIC knows the state or the LR
and only needs to clear the
active state when the pending bit in the LR is still set, where the
timer has to always clear it when returning from running the guest with
an injected timer interrupt.
Signed-off-by: Christoffer Dall <redacted>
Reviewed-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic-v2.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 23 +++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 7 +++++++
4 files changed, 88 insertions(+)
I am still unclear wrt ISPENDR case. Assume the guest writes into the
ISPENDR. So now we set the pending state on the phys distributor. The
SPI is acked by the host. I understand it becomes active (ie. not
pending and active). Then it gets injected into the guest and in the
above fold code the LR state is observed GICH_LR_PENDING_BIT. We are
going to read the physical pending state which is: not pending. So the
line level is low, the latch_pending is not used in mapped case and is
low. So to me the vIRQ is missed.
What do I miss?
Thanks
Eric
@@ -161,6 +181,15 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) val |= GICH_LR_EOI; }+ /*+ * Level-triggered mapped IRQs are special because we only observe+ * rising edges as input to the VGIC. We therefore lower the line+ * level here, so that we can take new virtual IRQs. See+ * vgic_v2_fold_lr_state for more info.+ */+ if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT))+ irq->line_level = false;+ /* The GICv2 LR only holds five bits of priority. */ val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
@@ -140,6 +140,29 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)kfree(irq);}+/* Get the input level of a mapped IRQ directly from the physical GIC */+boolvgic_get_phys_line_level(structvgic_irq*irq)+{+boolline_level;++BUG_ON(!irq->hw);++WARN_ON(irq_get_irqchip_state(irq->host_irq,+IRQCHIP_STATE_PENDING,+&line_level));+returnline_level;+}++/* Set/Clear the physical active state */+voidvgic_irq_set_phys_active(structvgic_irq*irq,boolactive)+{++BUG_ON(!irq->hw);+WARN_ON(irq_set_irqchip_state(irq->host_irq,+IRQCHIP_STATE_ACTIVE,+active));+}+/***kvm_vgic_target_oracle-computethetargetvcpuforanirq*
Hi Christoffer,
On 04/09/2017 12:24, Christoffer Dall wrote:
quoted hunk
The small indirection of a static function made the locking very obvious
but becomes pretty ugly as we start passing function pointer around.
Let's inline these two functions first to make the following patch more
readable.
Signed-off-by: Christoffer Dall <redacted>
Acked-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)
@@ -435,12 +435,17 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,return0;}-/* @irq->irq_lock must be held */
I chose to hold the lock outside of kvm_vgic_map/unmap_irq because in
kvm_vgic_set_forwarding(see https://lkml.org/lkml/2017/6/15/278) I was
also testing hw and target_vcpu fields. As you pointed out maybe I am
not obliged to check them but that was the rationale.
Thanks
Eric
quoted hunk
-static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
- unsigned int host_irq)
+int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,
+ u32 vintid)
{
+ struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);
struct irq_desc *desc;
struct irq_data *data;
+ int ret = 0;
+
+ BUG_ON(!irq);
+
+ spin_lock(&irq->irq_lock);
/*
* Find the physical IRQ number corresponding to @host_irq
@@ -448,7 +453,8 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, desc = irq_to_desc(host_irq); if (!desc) { kvm_err("%s: no interrupt descriptor\n", __func__);- return -EINVAL;+ ret = -EINVAL;+ goto out; } data = irq_desc_get_irq_data(desc); while (data->parent_data)
@@ -457,29 +463,10 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->hw = true; irq->host_irq = host_irq; irq->hwintid = data->hwirq;- return 0;-}--/* @irq->irq_lock must be held */-static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq)-{- irq->hw = false;- irq->hwintid = 0;-}--int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,- u32 vintid)-{- struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);- int ret;- BUG_ON(!irq);-- spin_lock(&irq->irq_lock);- ret = kvm_vgic_map_irq(vcpu, irq, host_irq);+out: spin_unlock(&irq->irq_lock); vgic_put_irq(vcpu->kvm, irq);- return ret; }
From: Christoffer Dall <hidden> Date: 2017-09-05 13:57:02
On Tue, Sep 05, 2017 at 11:38:48AM +0200, Auger Eric wrote:
Hi Christoffer,
On 04/09/2017 12:24, Christoffer Dall wrote:
quoted
Level-triggered mapped IRQs are special because we only observe rising
edges as input to the VGIC, and we don't set the EOI flag and therefore
are not told when the level goes down, so that we can re-queue a new
interrupt when the level goes up.
One way to solve this problem is to side-step the logic of the VGIC and
special case the validation in the injection path, but it has the
unfortunate drawback of having to peak into the physical GIC state
whenever we want to know if the interrupt is pending on the virtual
distributor.
Instead, we can maintain the current semantics of a level triggered
interrupt by sort of treating it as an edge-triggered interrupt,
following from the fact that we only observe an asserting edge. This
requires us to be a bit careful when populating the LRs and when folding
the state back in though:
* We lower the line level when populating the LR, so that when
subsequently observing an asserting edge, the VGIC will do the right
thing.
* If the guest never acked the interrupt while running (for example if
it had masked interrupts at the CPU level while running), we have
to preserve the pending state of the LR and move it back to the
line_level field of the struct irq when folding LR state.
If the guest never acked the interrupt while running, but changed the
device state and lowered the line (again with interrupts masked) then
we need to observe this change in the line_level.
Both of the above situations are solved by sampling the physical line
and set the line level when folding the LR back.
* Finally, if the guest never acked the interrupt while running and
sampling the line reveals that the device state has changed and the
line has been lowered, we must clear the physical active state, since
we will otherwise never be told when the interrupt becomes asserted
again.
This has the added benefit of making the timer optimization patches
(https://lists.cs.columbia.edu/pipermail/kvmarm/2017-July/026343.html) a
bit simpler, because the timer code doesn't have to clear the active
state on the sync anymore. It also potentially improves the performance
of the timer implementation because the GIC knows the state or the LR
and only needs to clear the
active state when the pending bit in the LR is still set, where the
timer has to always clear it when returning from running the guest with
an injected timer interrupt.
Signed-off-by: Christoffer Dall <redacted>
Reviewed-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic-v2.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic-v3.c | 29 +++++++++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.c | 23 +++++++++++++++++++++++
virt/kvm/arm/vgic/vgic.h | 7 +++++++
4 files changed, 88 insertions(+)
I am still unclear wrt ISPENDR case. Assume the guest writes into the
ISPENDR. So now we set the pending state on the phys distributor. The
SPI is acked by the host. I understand it becomes active (ie. not
pending and active). Then it gets injected into the guest and in the
above fold code the LR state is observed GICH_LR_PENDING_BIT. We are
going to read the physical pending state which is: not pending. So the
line level is low, the latch_pending is not used in mapped case and is
low. So to me the vIRQ is missed.
What do I miss?
You're not missing anything. It was supposed to set pending first then
followed by active. I can fix that.
Thanks,
-Christoffer
@@ -161,6 +181,15 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) val |= GICH_LR_EOI; }+ /*+ * Level-triggered mapped IRQs are special because we only observe+ * rising edges as input to the VGIC. We therefore lower the line+ * level here, so that we can take new virtual IRQs. See+ * vgic_v2_fold_lr_state for more info.+ */+ if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT))+ irq->line_level = false;+ /* The GICv2 LR only holds five bits of priority. */ val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
@@ -140,6 +140,29 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)kfree(irq);}+/* Get the input level of a mapped IRQ directly from the physical GIC */+boolvgic_get_phys_line_level(structvgic_irq*irq)+{+boolline_level;++BUG_ON(!irq->hw);++WARN_ON(irq_get_irqchip_state(irq->host_irq,+IRQCHIP_STATE_PENDING,+&line_level));+returnline_level;+}++/* Set/Clear the physical active state */+voidvgic_irq_set_phys_active(structvgic_irq*irq,boolactive)+{++BUG_ON(!irq->hw);+WARN_ON(irq_set_irqchip_state(irq->host_irq,+IRQCHIP_STATE_ACTIVE,+active));+}+/***kvm_vgic_target_oracle-computethetargetvcpuforanirq*
From: Christoffer Dall <hidden> Date: 2017-09-05 14:00:24
On Tue, Sep 05, 2017 at 12:26:14PM +0200, Auger Eric wrote:
Hi Christoffer,
On 04/09/2017 12:24, Christoffer Dall wrote:
quoted
The small indirection of a static function made the locking very obvious
but becomes pretty ugly as we start passing function pointer around.
Let's inline these two functions first to make the following patch more
readable.
Signed-off-by: Christoffer Dall <redacted>
Acked-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)
@@ -435,12 +435,17 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,return0;}-/* @irq->irq_lock must be held */
I chose to hold the lock outside of kvm_vgic_map/unmap_irq because in
kvm_vgic_set_forwarding(see https://lkml.org/lkml/2017/6/15/278) I was
also testing hw and target_vcpu fields. As you pointed out maybe I am
not obliged to check them but that was the rationale.
Ah ok, I see, you want to reuse this bit of code and the caller will
already be holding the spin-lock?
I can rework it then to pass the callback in kvm_vgic_map_irq. Would
that fit better with your subsequent patches?
Thanks,
-Christoffer
quoted
-static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
- unsigned int host_irq)
+int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,
+ u32 vintid)
{
+ struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);
struct irq_desc *desc;
struct irq_data *data;
+ int ret = 0;
+
+ BUG_ON(!irq);
+
+ spin_lock(&irq->irq_lock);
/*
* Find the physical IRQ number corresponding to @host_irq
@@ -448,7 +453,8 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, desc = irq_to_desc(host_irq); if (!desc) { kvm_err("%s: no interrupt descriptor\n", __func__);- return -EINVAL;+ ret = -EINVAL;+ goto out; } data = irq_desc_get_irq_data(desc); while (data->parent_data)
@@ -457,29 +463,10 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->hw = true; irq->host_irq = host_irq; irq->hwintid = data->hwirq;- return 0;-}--/* @irq->irq_lock must be held */-static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq)-{- irq->hw = false;- irq->hwintid = 0;-}--int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,- u32 vintid)-{- struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);- int ret;- BUG_ON(!irq);-- spin_lock(&irq->irq_lock);- ret = kvm_vgic_map_irq(vcpu, irq, host_irq);+out: spin_unlock(&irq->irq_lock); vgic_put_irq(vcpu->kvm, irq);- return ret; }
Hi Christoffer,
On 05/09/2017 16:00, Christoffer Dall wrote:
On Tue, Sep 05, 2017 at 12:26:14PM +0200, Auger Eric wrote:
quoted
Hi Christoffer,
On 04/09/2017 12:24, Christoffer Dall wrote:
quoted
The small indirection of a static function made the locking very obvious
but becomes pretty ugly as we start passing function pointer around.
Let's inline these two functions first to make the following patch more
readable.
Signed-off-by: Christoffer Dall <redacted>
Acked-by: Marc Zyngier <redacted>
---
virt/kvm/arm/vgic/vgic.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)
@@ -435,12 +435,17 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,return0;}-/* @irq->irq_lock must be held */
I chose to hold the lock outside of kvm_vgic_map/unmap_irq because in
kvm_vgic_set_forwarding(see https://lkml.org/lkml/2017/6/15/278) I was
also testing hw and target_vcpu fields. As you pointed out maybe I am
not obliged to check them but that was the rationale.
Ah ok, I see, you want to reuse this bit of code and the caller will
already be holding the spin-lock?
I can rework it then to pass the callback in kvm_vgic_map_irq. Would
that fit better with your subsequent patches?
Yes it would.
Thanks
Eric
Thanks,
-Christoffer
quoted
quoted
-static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
- unsigned int host_irq)
+int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,
+ u32 vintid)
{
+ struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);
struct irq_desc *desc;
struct irq_data *data;
+ int ret = 0;
+
+ BUG_ON(!irq);
+
+ spin_lock(&irq->irq_lock);
/*
* Find the physical IRQ number corresponding to @host_irq
@@ -448,7 +453,8 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, desc = irq_to_desc(host_irq); if (!desc) { kvm_err("%s: no interrupt descriptor\n", __func__);- return -EINVAL;+ ret = -EINVAL;+ goto out; } data = irq_desc_get_irq_data(desc); while (data->parent_data)
@@ -457,29 +463,10 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->hw = true; irq->host_irq = host_irq; irq->hwintid = data->hwirq;- return 0;-}--/* @irq->irq_lock must be held */-static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq)-{- irq->hw = false;- irq->hwintid = 0;-}--int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,- u32 vintid)-{- struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid);- int ret;- BUG_ON(!irq);-- spin_lock(&irq->irq_lock);- ret = kvm_vgic_map_irq(vcpu, irq, host_irq);+out: spin_unlock(&irq->irq_lock); vgic_put_irq(vcpu->kvm, irq);- return ret; }