--- /dev/null
+From stable+bounces-186326-greg=kroah.com@vger.kernel.org Fri Oct 17 15:41:25 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Oct 2025 09:41:17 -0400
+Subject: xen/events: Update virq_to_irq on migration
+To: stable@vger.kernel.org
+Cc: Jason Andryuk <jason.andryuk@amd.com>, Juergen Gross <jgross@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251017134117.3957210-1-sashal@kernel.org>
+
+From: Jason Andryuk <jason.andryuk@amd.com>
+
+[ Upstream commit 3fcc8e146935415d69ffabb5df40ecf50e106131 ]
+
+VIRQs come in 3 flavors, per-VPU, per-domain, and global, and the VIRQs
+are tracked in per-cpu virq_to_irq arrays.
+
+Per-domain and global VIRQs must be bound on CPU 0, and
+bind_virq_to_irq() sets the per_cpu virq_to_irq at registration time
+Later, the interrupt can migrate, and info->cpu is updated. When
+calling __unbind_from_irq(), the per-cpu virq_to_irq is cleared for a
+different cpu. If bind_virq_to_irq() is called again with CPU 0, the
+stale irq is returned. There won't be any irq_info for the irq, so
+things break.
+
+Make xen_rebind_evtchn_to_cpu() update the per_cpu virq_to_irq mappings
+to keep them update to date with the current cpu. This ensures the
+correct virq_to_irq is cleared in __unbind_from_irq().
+
+Fixes: e46cdb66c8fc ("xen: event channels")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
+Reviewed-by: Juergen Gross <jgross@suse.com>
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Message-ID: <20250828003604.8949-4-jason.andryuk@amd.com>
+[ Adjust context ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/xen/events/events_base.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/drivers/xen/events/events_base.c
++++ b/drivers/xen/events/events_base.c
+@@ -1807,9 +1807,20 @@ static int xen_rebind_evtchn_to_cpu(stru
+ * virq or IPI channel, which don't actually need to be rebound. Ignore
+ * it, but don't do the xenlinux-level rebind in that case.
+ */
+- if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0)
++ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) {
++ int old_cpu = info->cpu;
++
+ bind_evtchn_to_cpu(evtchn, tcpu, false);
+
++ if (info->type == IRQT_VIRQ) {
++ int virq = info->u.virq;
++ int irq = per_cpu(virq_to_irq, old_cpu)[virq];
++
++ per_cpu(virq_to_irq, old_cpu)[virq] = -1;
++ per_cpu(virq_to_irq, tcpu)[virq] = irq;
++ }
++ }
++
+ do_unmask(info, EVT_MASK_REASON_TEMPORARY);
+
+ return 0;