From: Sascha Bischoff <Sascha.Bischoff@arm.com>
To: "maz@kernel.org" <maz@kernel.org>
Cc: "yuzenghui@huawei.com" <yuzenghui@huawei.com>,
Timothy Hayes <Timothy.Hayes@arm.com>,
Suzuki Poulose <Suzuki.Poulose@arm.com>, nd <nd@arm.com>,
"peter.maydell@linaro.org" <peter.maydell@linaro.org>,
"kvmarm@lists.linux.dev" <kvmarm@lists.linux.dev>,
"jonathan.cameron@huawei.com" <jonathan.cameron@huawei.com>,
"linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>,
"kvm@vger.kernel.org" <kvm@vger.kernel.org>,
Joey Gouly <Joey.Gouly@arm.com>,
"lpieralisi@kernel.org" <lpieralisi@kernel.org>,
"oliver.upton@linux.dev" <oliver.upton@linux.dev>
Subject: Re: [PATCH v6 22/39] KVM: arm64: gic-v5: Check for pending PPIs
Date: Thu, 19 Mar 2026 08:27:03 +0000 [thread overview]
Message-ID: <fdda7f48bbc51b9599aac810ce699fd925cf7a8a.camel@arm.com> (raw)
In-Reply-To: <86fr5y5qoz.wl-maz@kernel.org>
On Tue, 2026-03-17 at 17:08 +0000, Marc Zyngier wrote:
> On Tue, 17 Mar 2026 11:45:41 +0000,
> Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> >
> > This change allows KVM to check for pending PPI interrupts. This
> > has
> > two main components:
> >
> > First of all, the effective priority mask is calculated. This is a
> > combination of the priority mask in the VPEs ICC_PCR_EL1.PRIORITY
> > and
> > the currently running priority as determined from the VPE's
> > ICH_APR_EL1. If an interrupt's priority is greater than or equal to
> > the effective priority mask, it can be signalled. Otherwise, it
> > cannot.
> >
> > Secondly, any Enabled and Pending PPIs must be checked against this
> > compound priority mask. The reqires the PPI priorities to by synced
> > back to the KVM shadow state on WFI entry - this is skipped in
> > general
> > operation as it isn't required and is rather expensive. If any
> > Enabled
> > and Pending PPIs are of sufficient priority to be signalled, then
> > there are pending PPIs. Else, there are not. This ensures that a
> > VPE
> > is not woken when it cannot actually process the pending
> > interrupts.
> >
> > As the PPI priorities are not synced back to the KVM shadow state
> > on
> > every guest exit, they must by synced prior to checking if there
> > are
> > pending interrupts for the guest. The sync itself happens in
> > vgic_v5_put() if, and only if, the vcpu is entering WFI as this is
> > the
> > only case where it is not planned to run the vcpu thread again. If
> > the
> > vcpu enters WFI, the vcpu thread will be descheduled and won't be
> > rescheduled again until it has a pending interrupt, which is
> > checked
> > from kvm_arch_vcpu_runnable().
> >
> > Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> > Reviewed-by: Joey Gouly <joey.gouly@arm.com>
> > Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> > ---
> > arch/arm64/kvm/vgic/vgic-v5.c | 101
> > ++++++++++++++++++++++++++++++++++
> > arch/arm64/kvm/vgic/vgic.c | 3 +
> > arch/arm64/kvm/vgic/vgic.h | 1 +
> > 3 files changed, 105 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/vgic/vgic-v5.c
> > b/arch/arm64/kvm/vgic/vgic-v5.c
> > index e080fce61dc35..14dba634f79b4 100644
> > --- a/arch/arm64/kvm/vgic/vgic-v5.c
> > +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> > @@ -122,6 +122,29 @@ int vgic_v5_finalize_ppi_state(struct kvm
> > *kvm)
> > return 0;
> > }
> >
> > +static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu
> > *vcpu)
> > +{
> > + struct vgic_v5_cpu_if *cpu_if = &vcpu-
> > >arch.vgic_cpu.vgic_v5;
> > + u32 highest_ap, priority_mask;
> > +
> > + /*
> > + * Counting the number of trailing zeros gives the current
> > active
> > + * priority. Explicitly use the 32-bit version here as we
> > have 32
> > + * priorities. 32 then means that there are no active
> > priorities.
> > + */
> > + highest_ap = cpu_if->vgic_apr ? __builtin_ctz(cpu_if-
> > >vgic_apr) : 32;
> > +
> > + /*
> > + * An interrupt is of sufficient priority if it is equal
> > to or
> > + * greater than the priority mask. Add 1 to the priority
> > mask
> > + * (i.e., lower priority) to match the APR logic before
> > taking
> > + * the min. This gives us the lowest priority that is
> > masked.
> > + */
> > + priority_mask = FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_VPMR,
> > cpu_if->vgic_vmcr);
> > +
> > + return min(highest_ap, priority_mask + 1);
> > +}
> > +
> > /*
> > * For GICv5, the PPIs are mostly directly managed by the
> > hardware. We (the
> > * hypervisor) handle the pending, active, enable state
> > save/restore, but don't
> > @@ -172,6 +195,80 @@ void vgic_v5_set_ppi_ops(struct vgic_irq *irq)
> > irq->ops = &vgic_v5_ppi_irq_ops;
> > }
> >
> > +/*
> > + * Sync back the PPI priorities to the vgic_irq shadow state for
> > any interrupts
> > + * exposed to the guest (skipping all others).
> > + */
> > +static void vgic_v5_sync_ppi_priorities(struct kvm_vcpu *vcpu)
> > +{
> > + struct vgic_v5_cpu_if *cpu_if = &vcpu-
> > >arch.vgic_cpu.vgic_v5;
> > + u64 priorityr;
> > + int i;
> > +
> > + /*
> > + * We have up to 16 PPI Priority regs, but only have a few
> > interrupts
> > + * that the guest is allowed to use. Limit our sync of PPI
> > priorities to
> > + * those actually exposed to the guest by first iterating
> > over the mask
> > + * of exposed PPIs.
> > + */
> > + for_each_set_bit(i, vcpu->kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) {
> > + u32 intid = vgic_v5_make_ppi(i);
> > + struct vgic_irq *irq;
> > + int pri_idx, pri_reg;
> > + u8 priority;
> > +
> > + /*
> > + * Determine which priority register and the field
> > within it to
> > + * extract.
> > + */
> > + pri_reg = i / 8;
> > + pri_idx = i % 8;
> > +
> > + priorityr = cpu_if->vgic_ppi_priorityr[pri_reg];
> > + priority = (priorityr >> (pri_idx * 8)) &
> > GENMASK(4, 0);
>
> It should be able to write this as:
>
> pri_bit = pri_idx * 8;
> priority = field_get(GENMASK(pri_bit + 4, pri_bit),
> priorityr);
>
> which while more verbose, clearly shows that you are extracting a
> field from the register.
Yeah, that's definitely better. Thanks.
>
> > +
> > + irq = vgic_get_vcpu_irq(vcpu, intid);
> > +
> > + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
> > + irq->priority = priority;
> > +
> > + vgic_put_irq(vcpu->kvm, irq);
> > + }
> > +}
> > +
> > +bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu)
> > +{
> > + unsigned int priority_mask;
> > + int i;
> > +
> > + priority_mask = vgic_v5_get_effective_priority_mask(vcpu);
> > +
> > + /* If the combined priority mask is 0, nothing can be
> > signalled! */
> > + if (!priority_mask)
> > + return false;
>
> The other case when nothing can be signalled is when ICH_VMCR_EL2.En
> == 0, meaning that the guest hasn't enabled interrupts at all.
>
> This should be taken into account, or a trapping WFI is going to turn
> into a nice CPU hog.
Very valid point.
There are two options for this. The ICH_VMCR_EL2 contains the En bit
(which is an alias of ICH_CR0_EL1.EN, i.e., is set/cleared when the
guest enables/disables interrupt delivery for a vcpu).
The first would be to explicitly check this bit when determining if
there are pending PPIs for a vcpu. However, this would need to be
checked in multiple places as the code evolves. One of these cases
would be when requesting a VPE Doorbell.
For both PPIs and VPE Doorbells, one needs to figure out the threshold
for an interrupt signalling. Therefore, I think it makes more sense to
roll this into the calculation of the priority mask. Effectively, if a
vcpu has not opted into interrupt delivery, the effective running
priority is the highest priority and nothing can signal. This is the
second option.
I am proposing this change:
diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
index 22230e6eaa8bb..450960b792331 100644
--- a/arch/arm64/kvm/vgic/vgic-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-v5.c
@@ -127,6 +127,14 @@ static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu *vcpu)
struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5;
u32 highest_ap, priority_mask;
+ /*
+ * If the guest's CPU has not opted to receive interrupts, then the
+ * effective running priority is the highest priority. Just return 0
+ * (the highest priority).
+ */
+ if (!FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_EN, cpu_if->vgic_vmcr))
+ return 0;
+
/*
* Counting the number of trailing zeros gives the current active
* priority. Explicitly use the 32-bit version here as we have 32
@@ -237,7 +245,12 @@ bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu)
priority_mask = vgic_v5_get_effective_priority_mask(vcpu);
- /* If the combined priority mask is 0, nothing can be signalled! */
+ /*
+ * If the combined priority mask is 0, nothing can be signalled! In the
+ * case where the guest has disabled interrupt delivery for the vcpu
+ * (via ICV_CR0_EL1.EN->ICH_VMCR_EL2.EN), we calculate the priority mask
+ * as 0 too (the highest possible priority).
+ */
if (!priority_mask)
return false;
>
> > +
> > + for_each_set_bit(i, vcpu->kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) {
> > + u32 intid = vgic_v5_make_ppi(i);
> > + bool has_pending = false;
> > + struct vgic_irq *irq;
> > +
> > + irq = vgic_get_vcpu_irq(vcpu, intid);
> > +
> > + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
> > {
> > + if (irq->enabled && irq_is_pending(irq) &&
> > + irq->priority <= priority_mask)
> > + has_pending = true;
> > + }
>
> nit:
> scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
> has_pending = (irq->enabled &&
> irq_is_pending(irq) &&
> irq->priority <=
> priority_mask);
Done
Thanks,
Sascha
>
> > +
> > + vgic_put_irq(vcpu->kvm, irq);
> > +
> > + if (has_pending)
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > /*
> > * Detect any PPIs state changes, and propagate the state with
> > KVM's
> > * shadow structures.
> > @@ -299,6 +396,10 @@ void vgic_v5_put(struct kvm_vcpu *vcpu)
> > kvm_call_hyp(__vgic_v5_save_apr, cpu_if);
> >
> > cpu_if->gicv5_vpe.resident = false;
> > +
> > + /* The shadow priority is only updated on entering WFI */
> > + if (vcpu_get_flag(vcpu, IN_WFI))
> > + vgic_v5_sync_ppi_priorities(vcpu);
> > }
> >
> > void vgic_v5_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr
> > *vmcrp)
> > diff --git a/arch/arm64/kvm/vgic/vgic.c
> > b/arch/arm64/kvm/vgic/vgic.c
> > index 3b148d3d4875e..d448205d80617 100644
> > --- a/arch/arm64/kvm/vgic/vgic.c
> > +++ b/arch/arm64/kvm/vgic/vgic.c
> > @@ -1230,6 +1230,9 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu
> > *vcpu)
> > unsigned long flags;
> > struct vgic_vmcr vmcr;
> >
> > + if (vgic_is_v5(vcpu->kvm))
> > + return vgic_v5_has_pending_ppi(vcpu);
> > +
> > if (!vcpu->kvm->arch.vgic.enabled)
> > return false;
> >
> > diff --git a/arch/arm64/kvm/vgic/vgic.h
> > b/arch/arm64/kvm/vgic/vgic.h
> > index ef4e3fb7159dd..3a9e610eefb00 100644
> > --- a/arch/arm64/kvm/vgic/vgic.h
> > +++ b/arch/arm64/kvm/vgic/vgic.h
> > @@ -365,6 +365,7 @@ void vgic_debug_destroy(struct kvm *kvm);
> >
> > int vgic_v5_probe(const struct gic_kvm_info *info);
> > void vgic_v5_set_ppi_ops(struct vgic_irq *irq);
> > +bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu);
> > void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu);
> > void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu);
> > void vgic_v5_load(struct kvm_vcpu *vcpu);
>
> Thanks,
>
> M.
>
next prev parent reply other threads:[~2026-03-19 8:28 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-17 11:39 [PATCH v6 00/39] KVM: arm64: Introduce vGIC-v5 with PPI support Sascha Bischoff
2026-03-17 11:40 ` [PATCH v6 01/39] KVM: arm64: vgic-v3: Drop userspace write sanitization for ID_AA64PFR0.GIC on GICv5 Sascha Bischoff
2026-03-19 10:02 ` Jonathan Cameron
2026-03-19 11:35 ` Sascha Bischoff
2026-03-20 10:27 ` Jonathan Cameron
2026-03-17 11:40 ` [PATCH v6 02/39] KVM: arm64: vgic: Rework vgic_is_v3() and add vgic_host_has_gicvX() Sascha Bischoff
2026-03-17 11:40 ` [PATCH v6 03/39] KVM: arm64: Return early from kvm_finalize_sys_regs() if guest has run Sascha Bischoff
2026-03-19 10:12 ` Jonathan Cameron
2026-03-19 11:41 ` Sascha Bischoff
2026-03-17 11:40 ` [PATCH v6 04/39] KVM: arm64: vgic: Split out mapping IRQs and setting irq_ops Sascha Bischoff
2026-03-17 16:00 ` Marc Zyngier
2026-03-18 17:30 ` Sascha Bischoff
2026-03-17 11:41 ` [PATCH v6 05/39] arm64/sysreg: Add remaining GICv5 ICC_ & ICH_ sysregs for KVM support Sascha Bischoff
2026-03-17 11:41 ` [PATCH v6 06/39] arm64/sysreg: Add GICR CDNMIA encoding Sascha Bischoff
2026-03-17 11:41 ` [PATCH v6 07/39] KVM: arm64: gic-v5: Add ARM_VGIC_V5 device to KVM headers Sascha Bischoff
2026-03-17 11:42 ` [PATCH v6 08/39] KVM: arm64: gic: Introduce interrupt type helpers Sascha Bischoff
2026-03-17 11:42 ` [PATCH v6 09/39] KVM: arm64: gic-v5: Add Arm copyright header Sascha Bischoff
2026-03-17 11:42 ` [PATCH v6 10/39] KVM: arm64: gic-v5: Detect implemented PPIs on boot Sascha Bischoff
2026-03-17 11:42 ` [PATCH v6 11/39] KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE Sascha Bischoff
2026-03-19 10:31 ` Jonathan Cameron
2026-03-19 14:02 ` Sascha Bischoff
2026-03-17 11:43 ` [PATCH v6 12/39] KVM: arm64: gic-v5: Support GICv5 FGTs & FGUs Sascha Bischoff
2026-03-17 11:43 ` [PATCH v6 13/39] KVM: arm64: gic-v5: Add emulation for ICC_IAFFIDR_EL1 accesses Sascha Bischoff
2026-03-19 10:34 ` Jonathan Cameron
2026-03-17 11:43 ` [PATCH v6 14/39] KVM: arm64: gic-v5: Trap and emulate ICC_IDR0_EL1 accesses Sascha Bischoff
2026-03-19 10:38 ` Jonathan Cameron
2026-03-17 11:43 ` [PATCH v6 15/39] KVM: arm64: gic-v5: Add vgic-v5 save/restore hyp interface Sascha Bischoff
2026-03-17 11:44 ` [PATCH v6 16/39] KVM: arm64: gic-v5: Implement GICv5 load/put and save/restore Sascha Bischoff
2026-03-17 11:44 ` [PATCH v6 17/39] KVM: arm64: gic-v5: Finalize GICv5 PPIs and generate mask Sascha Bischoff
2026-03-17 11:44 ` [PATCH v6 18/39] KVM: arm64: gic: Introduce queue_irq_unlock to irq_ops Sascha Bischoff
2026-03-17 11:44 ` [PATCH v6 19/39] KVM: arm64: gic-v5: Implement PPI interrupt injection Sascha Bischoff
2026-03-17 16:31 ` Marc Zyngier
2026-03-18 17:31 ` Sascha Bischoff
2026-03-17 11:45 ` [PATCH v6 20/39] KVM: arm64: gic-v5: Init Private IRQs (PPIs) for GICv5 Sascha Bischoff
2026-03-17 16:42 ` Marc Zyngier
2026-03-18 17:34 ` Sascha Bischoff
2026-03-17 11:45 ` [PATCH v6 21/39] KVM: arm64: gic-v5: Clear TWI if single task running Sascha Bischoff
2026-03-17 11:45 ` [PATCH v6 22/39] KVM: arm64: gic-v5: Check for pending PPIs Sascha Bischoff
2026-03-17 17:08 ` Marc Zyngier
2026-03-19 8:27 ` Sascha Bischoff [this message]
2026-03-17 11:45 ` [PATCH v6 23/39] KVM: arm64: gic-v5: Trap and mask guest ICC_PPI_ENABLERx_EL1 writes Sascha Bischoff
2026-03-17 11:46 ` [PATCH v6 24/39] KVM: arm64: Introduce set_direct_injection irq_op Sascha Bischoff
2026-03-17 11:46 ` [PATCH v6 25/39] KVM: arm64: gic-v5: Implement direct injection of PPIs Sascha Bischoff
2026-03-17 11:46 ` [PATCH v6 26/39] KVM: arm64: gic-v5: Support GICv5 interrupts with KVM_IRQ_LINE Sascha Bischoff
2026-03-17 11:46 ` [PATCH v6 27/39] KVM: arm64: gic-v5: Create and initialise vgic_v5 Sascha Bischoff
2026-03-17 11:47 ` [PATCH v6 28/39] KVM: arm64: gic-v5: Initialise ID and priority bits when resetting vcpu Sascha Bischoff
2026-03-17 11:47 ` [PATCH v6 29/39] KVM: arm64: gic-v5: Enlighten arch timer for GICv5 Sascha Bischoff
2026-03-17 18:05 ` Marc Zyngier
2026-03-19 8:59 ` Sascha Bischoff
2026-03-17 11:47 ` [PATCH v6 30/39] KVM: arm64: gic-v5: Mandate architected PPI for PMU emulation on GICv5 Sascha Bischoff
2026-03-17 11:48 ` [PATCH v6 31/39] KVM: arm64: gic: Hide GICv5 for protected guests Sascha Bischoff
2026-03-17 11:48 ` [PATCH v6 32/39] KVM: arm64: gic-v5: Hide FEAT_GCIE from NV GICv5 guests Sascha Bischoff
2026-03-17 11:48 ` [PATCH v6 33/39] KVM: arm64: gic-v5: Introduce kvm_arm_vgic_v5_ops and register them Sascha Bischoff
2026-03-17 11:48 ` [PATCH v6 34/39] KVM: arm64: gic-v5: Set ICH_VCTLR_EL2.En on boot Sascha Bischoff
2026-03-17 11:49 ` [PATCH v6 35/39] KVM: arm64: gic-v5: Probe for GICv5 device Sascha Bischoff
2026-03-18 15:34 ` Joey Gouly
2026-03-19 8:36 ` Sascha Bischoff
2026-03-17 11:49 ` [PATCH v6 36/39] Documentation: KVM: Introduce documentation for VGICv5 Sascha Bischoff
2026-03-17 11:49 ` [PATCH v6 37/39] KVM: arm64: gic-v5: Communicate userspace-driveable PPIs via a UAPI Sascha Bischoff
2026-03-17 11:49 ` [PATCH v6 38/39] KVM: arm64: selftests: Introduce a minimal GICv5 PPI selftest Sascha Bischoff
2026-03-17 11:50 ` [PATCH v6 39/39] KVM: arm64: selftests: Add no-vgic-v5 selftest Sascha Bischoff
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=fdda7f48bbc51b9599aac810ce699fd925cf7a8a.camel@arm.com \
--to=sascha.bischoff@arm.com \
--cc=Joey.Gouly@arm.com \
--cc=Suzuki.Poulose@arm.com \
--cc=Timothy.Hayes@arm.com \
--cc=jonathan.cameron@huawei.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=lpieralisi@kernel.org \
--cc=maz@kernel.org \
--cc=nd@arm.com \
--cc=oliver.upton@linux.dev \
--cc=peter.maydell@linaro.org \
--cc=yuzenghui@huawei.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.