Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Karl Mehltretter" <kmehltretter@gmail.com>
Cc: kvmarm@lists.linux.dev, kvm@vger.kernel.org,
	"Oliver Upton" <oupton@kernel.org>,
	"Marc Zyngier" <maz@kernel.org>
Subject: Re: [PATCH v2 1/3] KVM: arm64: vgic-v2: Don't drop pending writes to active level IRQs
Date: Wed, 28 Oct 2026 05:27:07 +0000	[thread overview]
Message-ID: <sashiko-outbox-187535@kernel.org> (raw)
In-Reply-To: <20261028050113.10046-2-kmehltretter@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] KVM: arm64: vgic-v2: GICH_LR_HW pending LR programmed with inactive phys IRQ
- [Medium] KVM: arm64: vgic-v2: spurious level IRQ after save/restore in vgic_v2_fold_lr()
--

commit 48710a44e389b45084a926f40b58ded3edf3a4dd
Author: Karl Mehltretter <kmehltretter@gmail.com>

KVM: arm64: vgic-v2: Don't drop pending writes to active level IRQs

This commit synchronizes the target vCPU's LRs in vgic_mmio_write_spending()
and preserves irq->pending_latch in vgic_v2_fold_lr() when a level-triggered
interrupt was already active, so that a pending state written via GICD_ISPENDR
while the interrupt is active is not dropped on deactivation.

> diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
> index 7182f63fc9382..f3ba3177dd97b 100644
> --- a/arch/arm64/kvm/vgic/vgic-v2.c
> +++ b/arch/arm64/kvm/vgic/vgic-v2.c
> @@ -57,7 +57,7 @@ static void vgic_v2_fold_lr(struct kvm_vcpu *vcpu, u32 val)
>  {
>  	u32 cpuid, intid = val & GICH_LR_VIRTUALID;
>  	struct vgic_irq *irq;
> -	bool deactivated;
> +	bool deactivated, was_active;
>  
>  	/* Extract the source vCPU id from the LR */
>  	cpuid = FIELD_GET(GICH_LR_PHYSID_CPUID, val) & 7;
> @@ -71,7 +71,8 @@ static void vgic_v2_fold_lr(struct kvm_vcpu *vcpu, u32 val)
>  
>  	scoped_guard(raw_spinlock, &irq->irq_lock) {
>  		/* Always preserve the active bit, note deactivation */
> -		deactivated = irq->active && !(val & GICH_LR_ACTIVE_BIT);
> +		was_active = irq->active;
> +		deactivated = was_active && !(val & GICH_LR_ACTIVE_BIT);
>  		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
>  
>  		if (irq->active && vgic_irq_is_sgi(intid))
> @@ -88,8 +89,11 @@ static void vgic_v2_fold_lr(struct kvm_vcpu *vcpu, u32 val)
>  
>  		/*
>  		 * Clear soft pending state when level irqs have been acked.
> +		 * A latch written while the irq was active is a new pending
> +		 * state and was not represented in the LR, so preserve it.
>  		 */
> -		if (irq->config == VGIC_CONFIG_LEVEL && !(val & GICH_LR_STATE))
> +		if (irq->config == VGIC_CONFIG_LEVEL &&
> +		    !(val & GICH_LR_PENDING_BIT) && !was_active)
>  			irq->pending_latch = false;

[Severity: High]
Can preserving irq->pending_latch here in vgic_v2_fold_lr() in
arch/arm64/kvm/vgic/vgic-v2.c cause a GICH_LR_HW list register to be
populated as pending while the physical interrupt is inactive in the host
distributor?

If the guest writes to GICD_ISPENDR0 for a mapped level-triggered interrupt
such as the architected timer PPI (irq->hw is true, irq->config is
VGIC_CONFIG_LEVEL, and !vgic_irq_needs_resampling(irq)) while that interrupt
is already active, vgic_mmio_write_spending() -> __set_pending() in
arch/arm64/kvm/vgic/vgic-mmio.c sets irq->pending_latch and marks the
physical interrupt active while the first instance is already active in
hardware:

arch/arm64/kvm/vgic/vgic-mmio.c:__set_pending() {
    ...
		irq->pending_latch = true;
		if (irq->hw && !is_user)
			vgic_irq_set_phys_active(irq, true);
    ...
}

When the guest deactivates that first in-flight instance, the hardware
deactivation clears the physical interrupt's active state in the host GIC
distributor. On the subsequent exit, vgic_v2_fold_lr() sees was_active == true
and deactivated == true, sets irq->active = false, and preserves
irq->pending_latch = true without re-activating the physical interrupt.

On the next guest entry, vgic_v2_populate_lr() -> vgic_v2_compute_lr() in
arch/arm64/kvm/vgic/vgic-v2.c sees irq->active == false and
irq_is_pending(irq) == true via irq->pending_latch:

arch/arm64/kvm/vgic/vgic-v2.c:vgic_v2_compute_lr() {
    ...
	if (irq->hw && !vgic_irq_needs_resampling(irq)) {
		val |= GICH_LR_HW;
		val |= irq->hwintid << GICH_LR_PHYSID_CPUID_SHIFT;
		/*
		 * Never set pending+active on a HW interrupt, as the
		 * pending state is kept at the physical distributor
		 * level.
		 */
		if (irq->active)
			allow_pending = false;
	}
    ...
}

Wouldn't this program the list register with GICH_LR_HW | GICH_LR_PENDING_BIT
while the corresponding physical interrupt in the host distributor is
inactive?

[Severity: Medium]
Also, what happens across a GICv2 VM save and restore when a level-triggered
device interrupt is active and its input line is still asserted
(irq->active == true, irq->line_level == true, and irq->pending_latch ==
false)?

When userspace saves KVM_DEV_TYPE_ARM_VGIC_V2 distributor state via
vgic_uaccess_read_pending() -> __read_pending() in
arch/arm64/kvm/vgic/vgic-mmio.c, GICv2 reads irq_is_pending(irq):

arch/arm64/kvm/vgic/vgic-mmio.c:__read_pending() {
    ...
			switch (vcpu->kvm->arch.vgic.vgic_model) {
			case KVM_DEV_TYPE_ARM_VGIC_V3:
				if (is_user) {
					val = irq->pending_latch;
					break;
				}
				fallthrough;
			default:
				val = irq_is_pending(irq);
				break;
			}
    ...
}

Because irq->line_level is true, the saved pending state is 1. On restore,
userspace restores the active state via vgic_mmio_uaccess_write_sactive()
(setting irq->active = true) and restores the pending state via
vgic_uaccess_write_spending() -> __set_pending() (setting irq->pending_latch =
true).

When the guest resumes, clears the interrupt condition at the device (setting
irq->line_level = false), and writes GICC_EOIR to deactivate the interrupt,
vgic_v2_fold_lr() runs with was_active == true and !(val & GICH_LR_STATE).
With the new !was_active check in vgic_v2_fold_lr(), won't irq->pending_latch
remain true on deactivation and cause a spurious second delivery of the level
interrupt?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028050113.10046-1-kmehltretter@gmail.com?part=1

  reply	other threads:[~2026-10-28  5:27 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28  5:01 [PATCH v2 0/3] KVM: arm64: Preserve level IRQ re-pending Karl Mehltretter
2026-10-28  5:01 ` [PATCH v2 1/3] KVM: arm64: vgic-v2: Don't drop pending writes to active level IRQs Karl Mehltretter
2026-10-28  5:27   ` sashiko-bot [this message]
2026-10-28  5:01 ` [PATCH v2 2/3] KVM: arm64: vgic-v3: " Karl Mehltretter
2026-10-28  5:26   ` sashiko-bot
2026-10-28  5:01 ` [PATCH v2 3/3] KVM: selftests: arm64: Test " Karl Mehltretter

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=sashiko-outbox-187535@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=kmehltretter@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=maz@kernel.org \
    --cc=oupton@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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