From: christoffer.dall@linaro.org (Christoffer Dall)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 14/27] arm/arm64: KVM: vgic: Improve handling of GICD_I{CS}PENDRn
Date: Fri, 26 Sep 2014 15:16:47 +0200 [thread overview]
Message-ID: <1411737420-9063-15-git-send-email-christoffer.dall@linaro.org> (raw)
In-Reply-To: <1411737420-9063-1-git-send-email-christoffer.dall@linaro.org>
Writes to GICD_ISPENDRn and GICD_ICPENDRn are currently not handled
correctly for level-triggered interrupts. The spec states that for
level-triggered interrupts, writes to the GICD_ISPENDRn activate the
output of a flip-flop which is in turn or'ed with the actual input
interrupt signal. Correspondingly, writes to GICD_ICPENDRn simply
deactivates the output of that flip-flop, but does not (of course) affect
the external input signal. Reads from GICC_IAR will also deactivate the
flip-flop output.
This requires us to track the state of the level-input separately from
the state in the flip-flop. We therefore introduce two new variables on
the distributor struct to track these two states. Astute readers may
notice that this is introducing more state than required (because an OR
of the two states gives you the pending state), but the remaining vgic
code uses the pending bitmap for optimized operations to figure out, at
the end of the day, if an interrupt is pending or not on the distributor
side. Refactoring the code to consider the two state variables all the
places where we currently access the precomputed pending value, did not
look pretty.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
include/kvm/arm_vgic.h | 16 ++++++-
virt/kvm/arm/vgic.c | 119 ++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 123 insertions(+), 12 deletions(-)
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 7d8e61f..f074539 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -140,9 +140,23 @@ struct vgic_dist {
/* Interrupt enabled (one bit per IRQ) */
struct vgic_bitmap irq_enabled;
- /* Interrupt state is pending on the distributor */
+ /* Level-triggered interrupt external input is asserted */
+ struct vgic_bitmap irq_level;
+
+ /*
+ * Interrupt state is pending on the distributor
+ */
struct vgic_bitmap irq_pending;
+ /*
+ * Tracks writes to GICD_ISPENDRn and GICD_ICPENDRn for level-triggered
+ * interrupts. Essentially holds the state of the flip-flop in
+ * Figure 4-10 on page 4-101 in ARM IHI 0048B.b.
+ * Once set, it is only cleared for level-triggered interrupts on
+ * guest ACKs (when we queue it) or writes to GICD_ICPENDRn.
+ */
+ struct vgic_bitmap irq_soft_pend;
+
/* Level-triggered interrupt queued on VCPU interface */
struct vgic_bitmap irq_queued;
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 2026b61..435d8e7 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -67,6 +67,11 @@
* - When the interrupt is EOIed, the maintenance interrupt fires,
* and clears the corresponding bit in irq_queued. This allows the
* interrupt line to be sampled again.
+ * - Note that level-triggered interrupts can also be set to pending from
+ * writes to GICD_ISPENDRn and lowering the external input line does not
+ * cause the interrupt to become inactive in such a situation.
+ * Conversely, writes to GICD_ICPENDRn do not cause the interrupt to become
+ * inactive as long as the external input line is held high.
*/
#define VGIC_ADDR_UNDEF (-1)
@@ -217,6 +222,41 @@ static void vgic_irq_clear_queued(struct kvm_vcpu *vcpu, int irq)
vgic_bitmap_set_irq_val(&dist->irq_queued, vcpu->vcpu_id, irq, 0);
}
+static int vgic_dist_irq_get_level(struct kvm_vcpu *vcpu, int irq)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ return vgic_bitmap_get_irq_val(&dist->irq_level, vcpu->vcpu_id, irq);
+}
+
+static void vgic_dist_irq_set_level(struct kvm_vcpu *vcpu, int irq)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ vgic_bitmap_set_irq_val(&dist->irq_level, vcpu->vcpu_id, irq, 1);
+}
+
+static void vgic_dist_irq_clear_level(struct kvm_vcpu *vcpu, int irq)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ vgic_bitmap_set_irq_val(&dist->irq_level, vcpu->vcpu_id, irq, 0);
+}
+
+static int vgic_dist_irq_soft_pend(struct kvm_vcpu *vcpu, int irq)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ return vgic_bitmap_get_irq_val(&dist->irq_soft_pend, vcpu->vcpu_id, irq);
+}
+
+static void vgic_dist_irq_clear_soft_pend(struct kvm_vcpu *vcpu, int irq)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ vgic_bitmap_set_irq_val(&dist->irq_soft_pend, vcpu->vcpu_id, irq, 0);
+}
+
static int vgic_dist_irq_is_pending(struct kvm_vcpu *vcpu, int irq)
{
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
@@ -414,11 +454,26 @@ static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
struct kvm_exit_mmio *mmio,
phys_addr_t offset)
{
- u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_pending,
- vcpu->vcpu_id, offset);
+ u32 *reg;
+ u32 level_mask;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ reg = vgic_bitmap_get_reg(&dist->irq_cfg, vcpu->vcpu_id, offset);
+ level_mask = (~(*reg));
+
+ /* Mark both level and edge triggered irqs as pending */
+ reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu->vcpu_id, offset);
vgic_reg_access(mmio, reg, offset,
ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+
if (mmio->is_write) {
+ /* Set the soft-pending flag only for level-triggered irqs */
+ reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
+ vcpu->vcpu_id, offset);
+ vgic_reg_access(mmio, reg, offset,
+ ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+ *reg &= level_mask;
+
vgic_update_state(vcpu->kvm);
return true;
}
@@ -430,11 +485,27 @@ static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
struct kvm_exit_mmio *mmio,
phys_addr_t offset)
{
- u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_pending,
- vcpu->vcpu_id, offset);
+ u32 *level_active;
+ u32 *reg;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu->vcpu_id, offset);
vgic_reg_access(mmio, reg, offset,
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
if (mmio->is_write) {
+ /* Re-set level triggered level-active interrupts */
+ level_active = vgic_bitmap_get_reg(&dist->irq_level,
+ vcpu->vcpu_id, offset);
+ reg = vgic_bitmap_get_reg(&dist->irq_pending,
+ vcpu->vcpu_id, offset);
+ *reg |= *level_active;
+
+ /* Clear soft-pending flags */
+ reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
+ vcpu->vcpu_id, offset);
+ vgic_reg_access(mmio, reg, offset,
+ ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
+
vgic_update_state(vcpu->kvm);
return true;
}
@@ -1268,17 +1339,32 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
for_each_set_bit(lr, eisr_ptr, vgic->nr_lr) {
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
+ WARN_ON(vgic_irq_is_edge(vcpu, vlr.irq));
vgic_irq_clear_queued(vcpu, vlr.irq);
WARN_ON(vlr.state & LR_STATE_MASK);
vlr.state = 0;
vgic_set_lr(vcpu, lr, vlr);
+ /*
+ * If the IRQ was EOIed it was also ACKed and we we
+ * therefore assume we can clear the soft pending
+ * state (should it had been set) for this interrupt.
+ *
+ * Note: if the IRQ soft pending state was set after
+ * the IRQ was acked, it actually shouldn't be
+ * cleared, but we have no way of knowing that unless
+ * we start trapping ACKs when the soft-pending state
+ * is set.
+ */
+ vgic_dist_irq_clear_soft_pend(vcpu, vlr.irq);
+
/* Any additional pending interrupt? */
- if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) {
+ if (vgic_dist_irq_get_level(vcpu, vlr.irq)) {
vgic_cpu_irq_set(vcpu, vlr.irq);
level_pending = true;
} else {
+ vgic_dist_irq_clear_pending(vcpu, vlr.irq);
vgic_cpu_irq_clear(vcpu, vlr.irq);
}
@@ -1384,17 +1470,19 @@ static void vgic_kick_vcpus(struct kvm *kvm)
static int vgic_validate_injection(struct kvm_vcpu *vcpu, int irq, int level)
{
int edge_triggered = vgic_irq_is_edge(vcpu, irq);
- int state = vgic_dist_irq_is_pending(vcpu, irq);
/*
* Only inject an interrupt if:
* - edge triggered and we have a rising edge
* - level triggered and we change level
*/
- if (edge_triggered)
+ if (edge_triggered) {
+ int state = vgic_dist_irq_is_pending(vcpu, irq);
return level > state;
- else
+ } else {
+ int state = vgic_dist_irq_get_level(vcpu, irq);
return level != state;
+ }
}
static bool vgic_update_irq_pending(struct kvm *kvm, int cpuid,
@@ -1424,10 +1512,19 @@ static bool vgic_update_irq_pending(struct kvm *kvm, int cpuid,
kvm_debug("Inject IRQ%d level %d CPU%d\n", irq_num, level, cpuid);
- if (level)
+ if (level) {
+ if (level_triggered)
+ vgic_dist_irq_set_level(vcpu, irq_num);
vgic_dist_irq_set_pending(vcpu, irq_num);
- else
- vgic_dist_irq_clear_pending(vcpu, irq_num);
+ } else {
+ if (level_triggered) {
+ vgic_dist_irq_clear_level(vcpu, irq_num);
+ if (!vgic_dist_irq_soft_pend(vcpu, irq_num))
+ vgic_dist_irq_clear_pending(vcpu, irq_num);
+ } else {
+ vgic_dist_irq_clear_pending(vcpu, irq_num);
+ }
+ }
enabled = vgic_irq_is_enabled(vcpu, irq_num);
--
2.0.0
next prev parent reply other threads:[~2014-09-26 13:16 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-26 13:16 [PATCH 00/27] Changes for arm/arm64 KVM for 3.18 Christoffer Dall
2014-09-26 13:16 ` [PATCH 01/27] KVM: Introduce gfn_to_hva_memslot_prot Christoffer Dall
2014-09-26 13:16 ` [PATCH 02/27] arm/arm64: KVM: Support KVM_CAP_READONLY_MEM Christoffer Dall
2014-09-26 13:16 ` [PATCH 03/27] KVM: ARM/arm64: fix non-const declaration of function returning const Christoffer Dall
2014-09-26 13:16 ` [PATCH 04/27] KVM: ARM/arm64: fix broken __percpu annotation Christoffer Dall
2014-09-26 13:16 ` [PATCH 05/27] KVM: ARM/arm64: avoid returning negative error code as bool Christoffer Dall
2014-09-26 13:16 ` [PATCH 06/27] KVM: ARM/arm64: return -EFAULT if copy_from_user fails in set_timer_reg Christoffer Dall
2014-09-26 13:16 ` [PATCH 07/27] KVM: vgic: return int instead of bool when checking I/O ranges Christoffer Dall
2014-09-26 13:16 ` [PATCH 08/27] KVM: vgic: declare probe function pointer as const Christoffer Dall
2014-09-26 13:16 ` [PATCH 09/27] ARM/arm64: KVM: fix use of WnR bit in kvm_is_write_fault() Christoffer Dall
2014-09-26 13:16 ` [PATCH 10/27] KVM: EVENTFD: remove inclusion of irq.h Christoffer Dall
2014-09-26 13:16 ` [PATCH 11/27] arm/arm64: KVM: Rename irq_state to irq_pending Christoffer Dall
2014-09-26 13:16 ` [PATCH 12/27] arm/arm64: KVM: Rename irq_active to irq_queued Christoffer Dall
2014-09-26 13:16 ` [PATCH 13/27] arm/arm64: KVM: vgic: Clear queued flags on unqueue Christoffer Dall
2014-09-26 13:16 ` Christoffer Dall [this message]
2014-09-26 13:16 ` [PATCH 15/27] arm/arm64: KVM: vgic: Fix SGI writes to GICD_I{CS}PENDR0 Christoffer Dall
2014-09-26 13:16 ` [PATCH 16/27] arm/arm64: KVM: vgic: Clarify and correct vgic documentation Christoffer Dall
2014-09-26 13:16 ` [PATCH 17/27] KVM: ARM: vgic: plug irq injection race Christoffer Dall
2014-09-26 13:16 ` [PATCH 18/27] arm/arm64: KVM: vgic: switch to dynamic allocation Christoffer Dall
2014-09-26 13:16 ` [PATCH 19/27] arm/arm64: KVM: vgic: Parametrize VGIC_NR_SHARED_IRQS Christoffer Dall
2014-09-26 13:16 ` [PATCH 20/27] arm/arm64: KVM: vgic: kill VGIC_MAX_CPUS Christoffer Dall
2014-09-26 13:16 ` [PATCH 21/27] arm/arm64: KVM: vgic: handle out-of-range MMIO accesses Christoffer Dall
2014-09-26 13:16 ` [PATCH 22/27] arm/arm64: KVM: vgic: kill VGIC_NR_IRQS Christoffer Dall
2014-09-26 13:16 ` [PATCH 23/27] arm/arm64: KVM: vgic: delay vgic allocation until init time Christoffer Dall
2014-09-26 13:16 ` [PATCH 24/27] arm/arm64: KVM: vgic: make number of irqs a configurable attribute Christoffer Dall
2014-09-26 13:16 ` [PATCH 25/27] arm/arm64: KVM: Fix set_clear_sgi_pend_reg offset Christoffer Dall
2014-09-26 13:16 ` [PATCH 26/27] arm/arm64: KVM: Fix VTTBR_BADDR_MASK and pgd alloc Christoffer Dall
2014-09-26 13:17 ` [PATCH 27/27] arm/arm64: KVM: Report correct FSC for unsupported fault types Christoffer Dall
2014-09-27 19:33 ` [PATCH 00/27] Changes for arm/arm64 KVM for 3.18 Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1411737420-9063-15-git-send-email-christoffer.dall@linaro.org \
--to=christoffer.dall@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).