--- /dev/null
+From 13031fb6b8357fbbcded2a7f4cba73e4781ee594 Mon Sep 17 00:00:00 2001
+From: Hyunwoo Kim <imv4bel@gmail.com>
+Date: Mon, 1 Jun 2026 23:53:26 +0900
+Subject: KVM: arm64: vgic-its: Drop the translation cache reference only for the erased entry
+
+From: Hyunwoo Kim <imv4bel@gmail.com>
+
+commit 13031fb6b8357fbbcded2a7f4cba73e4781ee594 upstream.
+
+vgic_its_invalidate_cache() walks the per-ITS translation cache with
+xa_for_each() and drops the cache's reference on each entry with
+vgic_put_irq(). It puts the iterated pointer, though, rather than the
+value returned by xa_erase().
+
+The function is called from contexts that do not exclude one another: the
+ITS command handlers hold its_lock, the GITS_CTLR write path holds
+cmd_lock, and the path that clears EnableLPIs in a redistributor's
+GICR_CTLR holds neither. Two or more of them can drain the same cache
+concurrently, and if each one observes the same entry, erases it and then
+puts it, the single reference the cache holds on that entry is dropped
+more than once. The entry can then be freed while an ITE still maps it.
+
+xa_erase() is atomic and returns the previous entry, so put only the entry
+that this context actually removed. The cache reference is then dropped
+exactly once per entry even when the invalidations run concurrently, and
+the behavior is unchanged when only one context runs.
+
+Fixes: 8201d1028caa ("KVM: arm64: vgic-its: Maintain a translation cache per ITS")
+Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
+Reviewed-by: Oliver Upton <oupton@kernel.org>
+Link: https://patch.msgid.link/ah2c5lu4JbUg7dj-@v4bel
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kvm/vgic/vgic-its.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/kvm/vgic/vgic-its.c
++++ b/arch/arm64/kvm/vgic/vgic-its.c
+@@ -597,8 +597,10 @@ static void vgic_its_invalidate_cache(st
+ unsigned long idx;
+
+ xa_for_each(&its->translation_cache, idx, irq) {
+- xa_erase(&its->translation_cache, idx);
+- vgic_put_irq(kvm, irq);
++ /* Only the context that erases the entry drops its cache ref. */
++ irq = xa_erase(&its->translation_cache, idx);
++ if (irq)
++ vgic_put_irq(kvm, irq);
+ }
+ }
+