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 29/39] KVM: arm64: gic-v5: Enlighten arch timer for GICv5
Date: Thu, 19 Mar 2026 08:59:40 +0000 [thread overview]
Message-ID: <9ce20b3090e60df254a4780f9eedb22ce56351aa.camel@arm.com> (raw)
In-Reply-To: <86ecli5o24.wl-maz@kernel.org>
On Tue, 2026-03-17 at 18:05 +0000, Marc Zyngier wrote:
> On Tue, 17 Mar 2026 11:47:29 +0000,
> Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> >
> > Now that GICv5 has arrived, the arch timer requires some TLC to
> > address some of the key differences introduced with GICv5.
> >
> > For PPIs on GICv5, the queue_irq_unlock irq_op is used as AP lists
> > are
> > not required at all for GICv5. The arch timer also introduces an
> > irq_op - get_input_level. Extend the arch-timer-provided irq_ops to
> > include the PPI op for vgic_v5 guests.
> >
> > When possible, DVI (Direct Virtual Interrupt) is set for PPIs when
> > using a vgic_v5, which directly inject the pending state into the
> > guest. This means that the host never sees the interrupt for the
> > guest
> > for these interrupts. This has three impacts.
> >
> > * First of all, the kvm_cpu_has_pending_timer check is updated to
> > explicitly check if the timers are expected to fire.
> >
> > * Secondly, for mapped timers (which use DVI) they must be masked
> > on
> > the host prior to entering a GICv5 guest, and unmasked on the
> > return
> > path. This is handled in set_timer_irq_phys_masked.
> >
> > * Thirdly, it makes zero sense to attempt to inject state for a
> > DVI'd
> > interrupt. Track which timers are direct, and skip the call to
> > kvm_vgic_inject_irq() for these.
> >
> > The final, but rather important, change is that the architected
> > PPIs
> > for the timers are made mandatory for a GICv5 guest. Attempts to
> > set
> > them to anything else are actively rejected. Once a vgic_v5 is
> > initialised, the arch timer PPIs are also explicitly reinitialised
> > to
> > ensure the correct GICv5-compatible PPIs are used - this also adds
> > in
> > the GICv5 PPI type to the intid.
> >
> > Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> > Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> > ---
> > arch/arm64/kvm/arch_timer.c | 110 ++++++++++++++++++++++++++--
> > ----
> > arch/arm64/kvm/vgic/vgic-init.c | 9 +++
> > arch/arm64/kvm/vgic/vgic-v5.c | 7 +-
> > include/kvm/arm_arch_timer.h | 11 +++-
> > include/kvm/arm_vgic.h | 3 +
> > 5 files changed, 115 insertions(+), 25 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/arch_timer.c
> > b/arch/arm64/kvm/arch_timer.c
> > index 53312b88c342d..4575c36cae537 100644
> > --- a/arch/arm64/kvm/arch_timer.c
> > +++ b/arch/arm64/kvm/arch_timer.c
> > @@ -56,6 +56,12 @@ static struct irq_ops arch_timer_irq_ops = {
> > .get_input_level = kvm_arch_timer_get_input_level,
> > };
> >
> > +static struct irq_ops arch_timer_irq_ops_vgic_v5 = {
> > + .get_input_level = kvm_arch_timer_get_input_level,
> > + .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,
> > + .set_direct_injection = vgic_v5_set_ppi_dvi,
> > +};
> > +
> > static int nr_timers(struct kvm_vcpu *vcpu)
> > {
> > if (!vcpu_has_nv(vcpu))
> > @@ -177,6 +183,10 @@ void get_timer_map(struct kvm_vcpu *vcpu,
> > struct timer_map *map)
> > map->emul_ptimer = vcpu_ptimer(vcpu);
> > }
> >
> > + map->direct_vtimer->direct = true;
> > + if (map->direct_ptimer)
> > + map->direct_ptimer->direct = true;
> > +
> > trace_kvm_get_timer_map(vcpu->vcpu_id, map);
> > }
> >
> > @@ -396,7 +406,11 @@ static bool kvm_timer_should_fire(struct
> > arch_timer_context *timer_ctx)
> >
> > int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
> > {
> > - return vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu)
> > == 0;
> > + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
> > + struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
> > +
> > + return kvm_timer_should_fire(vtimer) ||
> > kvm_timer_should_fire(ptimer) ||
> > + (vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu)
> > == 0);
> > }
> >
> > /*
> > @@ -447,6 +461,10 @@ static void kvm_timer_update_irq(struct
> > kvm_vcpu *vcpu, bool new_level,
> > if (userspace_irqchip(vcpu->kvm))
> > return;
> >
> > + /* Skip injecting on GICv5 for directly injected (DVI'd)
> > timers */
> > + if (vgic_is_v5(vcpu->kvm) && timer_ctx->direct)
> > + return;
> > +
> > kvm_vgic_inject_irq(vcpu->kvm, vcpu,
> > timer_irq(timer_ctx),
> > timer_ctx->irq.level,
> > @@ -657,6 +675,24 @@ static inline void
> > set_timer_irq_phys_active(struct arch_timer_context *ctx, boo
> > WARN_ON(r);
> > }
> >
> > +/*
> > + * On GICv5 we use DVI for the arch timer PPIs. This is restored
> > later
> > + * on as part of vgic_load. Therefore, in order to avoid the
> > guest's
> > + * interrupt making it to the host we mask it before entering the
> > + * guest and unmask it again when we return.
> > + */
> > +static inline void set_timer_irq_phys_masked(struct
> > arch_timer_context *ctx, bool masked)
> > +{
> > + if (masked) {
> > + disable_percpu_irq(ctx->host_timer_irq);
> > + } else {
> > + if (ctx->host_timer_irq == host_vtimer_irq)
> > + enable_percpu_irq(ctx->host_timer_irq,
> > host_vtimer_irq_flags);
> > + else
> > + enable_percpu_irq(ctx->host_timer_irq,
> > host_ptimer_irq_flags);
> > + }
> > +}
>
> I think this is missing a trick, which is to reuse the mask/unmask
> infrastructure we use for the fruity crap. How about this following
> untested hack?
>
> diff --git a/arch/arm64/kvm/arch_timer.c
> b/arch/arm64/kvm/arch_timer.c
> index 600f250753b45..b29bea800e2ab 100644
> --- a/arch/arm64/kvm/arch_timer.c
> +++ b/arch/arm64/kvm/arch_timer.c
> @@ -660,7 +660,7 @@ static inline void
> set_timer_irq_phys_active(struct arch_timer_context *ctx, boo
> static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx)
> {
> struct kvm_vcpu *vcpu = timer_context_to_vcpu(ctx);
> - bool phys_active = false;
> + bool phys_active = vgic_is_v5(vcpu->kvm);
Note: This needs to be or'd in later as it gets overwritten by
kvm_vgic_map_is_active().
>
> /*
> * Update the timer output so that it is likely to match the
> @@ -934,6 +934,12 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
>
> if (kvm_vcpu_is_blocking(vcpu))
> kvm_timer_blocking(vcpu);
> +
> + if (vgic_is_v5(vcpu)) {
> + set_timer_irq_phys_active(map.direct_vtimer, false);
> + if (map.direct_ptimer)
> + set_timer_irq_phys_active(map.direct_ptimer,
> false);
> + }
> }
>
> void kvm_timer_sync_nested(struct kvm_vcpu *vcpu)
> @@ -1333,7 +1339,8 @@ static int kvm_irq_init(struct
> arch_timer_kvm_info *info)
> host_vtimer_irq = info->virtual_irq;
> kvm_irq_fixup_flags(host_vtimer_irq,
> &host_vtimer_irq_flags);
>
> - if (kvm_vgic_global_state.no_hw_deactivation) {
> + if (kvm_vgic_global_state.no_hw_deactivation ||
> + kvm_vgic_global_state.type == VGIC_V5) {
> struct fwnode_handle *fwnode;
> struct irq_data *data;
>
> @@ -1351,7 +1358,8 @@ static int kvm_irq_init(struct
> arch_timer_kvm_info *info)
> return -ENOMEM;
> }
>
> - arch_timer_irq_ops.flags |= VGIC_IRQ_SW_RESAMPLE;
> + if (kvm_vgic_global_state.no_hw_deactivation)
> + arch_timer_irq_ops.flags |=
> VGIC_IRQ_SW_RESAMPLE;
> WARN_ON(irq_domain_push_irq(domain, host_vtimer_irq,
> (void *)TIMER_VTIMER));
> }
>
> which should avoid adding some new masking stuff.
Thanks for this, Marc. I've given it a go, and have eventually been
able to make it work. Things were, as they always are, a little more
complex.
First of all, the GICv5 irqchip driver doesn't register a
irq_set_type() handler for PPIs as those do not have a configurable
handling/trigger mode. I believe we originally had this in the
prototyping, but given that all it could do is to check that the
hardware matched whatever firmware said, it was dropped as part of
upstreaming. irq_set_type() is marked as optional in the genericirq
documentation, so this seemed like a fine thing to do.
However, as it turns out things fall over if one layers a domain on top
of a domain that doesn't implement irq_set_type() and calls
request_percpu_irq(). Somewhere in the depths of that,
__irq_set_trigger() is called, which returns -ENOSYS if the parent
domain doesn't have irq_set_type() populated.
This means that without having a irq_set_type() in the GICv5 irqchip
driver, we bail out in kvm_timer_hyp_init() with your above change.
I'm not sure if this is a deficiency in the GICv5 irqchip driver, or if
it is one in the irqchip subsystem itself. As I said, the function is
marked as optional in the documentation (Documentation/core-
api/genericirq.rst), and this suggests to me that it isn't in the case
where one has a domain hierarchy rather than a single flat domain.
I worked around this with:
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-
v5.c
index 405a5eee847b6..6b0903be8ebfd 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -511,6 +511,23 @@ static bool gicv5_ppi_irq_is_level(irq_hw_number_t
hwirq)
return !!(read_ppi_sysreg_s(hwirq, PPI_HM) & bit);
}
+static int gicv5_ppi_irq_set_type(struct irq_data *d, unsigned int
type)
+{
+ /*
+ * GICv5's PPIs do not have a configurable trigger or handling
+ * mode. Check that the attempt to set a type matches what the
+ * hardware reports in the HMR, and error on a mismatch.
+ */
+
+ if (type & IRQ_TYPE_EDGE_BOTH && gicv5_ppi_irq_is_level(d-
>hwirq))
+ return -EINVAL;
+
+ if (type & IRQ_TYPE_LEVEL_MASK && !gicv5_ppi_irq_is_level(d-
>hwirq))
+ return -EINVAL;
+
+ return 0;
+}
+
static int gicv5_ppi_irq_set_vcpu_affinity(struct irq_data *d, void
*vcpu)
{
if (vcpu)
@@ -526,6 +543,7 @@ static const struct irq_chip gicv5_ppi_irq_chip = {
.irq_mask = gicv5_ppi_irq_mask,
.irq_unmask = gicv5_ppi_irq_unmask,
.irq_eoi = gicv5_ppi_irq_eoi,
+ .irq_set_type = gicv5_ppi_irq_set_type,
.irq_get_irqchip_state = gicv5_ppi_irq_get_irqchip_state,
.irq_set_irqchip_state = gicv5_ppi_irq_set_irqchip_state,
.irq_set_vcpu_affinity = gicv5_ppi_irq_set_vcpu_affinity,
It is noddy, but it "fixes" the issue when requesting an irq.
The next issue is around EOIing. When running GICv3 guests that make
use of the HW bit in the LRs and hence rely on hw deactivation on a
GICv5 host we handle this in the host irqchip driver. Specifically, we
do the following for PPIs:
static void gicv5_ppi_irq_eoi(struct irq_data *d)
{
/* Skip deactivate for forwarded PPI interrupts */
if (irqd_is_forwarded_to_vcpu(d)) {
gic_insn(0, CDEOI);
return;
}
gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
}
The arch_timer irqchip's EOI as it currently stands completely skips
the EOI callback for forwarded irqs. This doesn't work for GICv3 guests
on GICv5 as that means they never get EOI'd as the we emulate that in
software. Therefore, one needs to explicitly catch that case, and call
the host irqchip driver's EOI on GICv5 hosts:
static void timer_irq_eoi(struct irq_data *d)
{
- if (!irqd_is_forwarded_to_vcpu(d))
+ /*
+ * On a GICv5 host, we still need to call EOI on the parent for
+ * PPIs. The host driver already handles irqs which are forwarded to
+ * vcpus, and skips the GIC CDDI while still doing the GIC CDEOI. This
+ * is required to emulate the EOIMode=1 on GICv5 hardware. Failure to
+ * call EOI unsurprisingly results in *BAD* lock-ups.
+ */
+ if (!irqd_is_forwarded_to_vcpu(d) ||
+ kvm_vgic_global_state.type == VGIC_V5)
irq_chip_eoi_parent(d);
}
In the end after making these changes, I've been able to get this
working for the arch_timer code, and can completely remove the bespoke
GICv5 masking.
Thanks,
Sascha
>
> Thanks,
>
> M.
>
next prev parent reply other threads:[~2026-03-19 9:00 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
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 [this message]
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=9ce20b3090e60df254a4780f9eedb22ce56351aa.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.