--- /dev/null
+From 0240fa18d247c99a1967f2fed025296a89a1c5f5 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 7 Jan 2025 18:32:29 +0100
+Subject: accel/ivpu: Dump only first MMU fault from single context
+
+From: Karol Wachowski <karol.wachowski@intel.com>
+
+commit 0240fa18d247c99a1967f2fed025296a89a1c5f5 upstream.
+
+Stop dumping consecutive faults from an already faulty context immediately,
+instead of waiting for the context abort thread handler (IRQ handler bottom
+half) to abort currently executing jobs.
+
+Remove 'R' (record events) bit from context descriptor of a faulty
+context to prevent future faults generation.
+
+This change speeds up the IRQ handler by eliminating the need to print the
+fault content repeatedly. Additionally, it prevents flooding dmesg with
+errors, which was occurring due to the delay in the bottom half of the
+handler stopping fault-generating jobs.
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-7-maciej.falkowski@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_mmu.c | 51 ++++++++++++++++++++++++++++++----
+ drivers/accel/ivpu/ivpu_mmu_context.c | 13 --------
+ drivers/accel/ivpu/ivpu_mmu_context.h | 2 -
+ 3 files changed, 46 insertions(+), 20 deletions(-)
+
+--- a/drivers/accel/ivpu/ivpu_mmu.c
++++ b/drivers/accel/ivpu/ivpu_mmu.c
+@@ -870,23 +870,64 @@ static u32 *ivpu_mmu_get_event(struct iv
+ return evt;
+ }
+
++static int ivpu_mmu_disable_events(struct ivpu_device *vdev, u32 ssid)
++{
++ struct ivpu_mmu_info *mmu = vdev->mmu;
++ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
++ u64 *entry;
++ u64 val;
++
++ if (ssid > IVPU_MMU_CDTAB_ENT_COUNT)
++ return -EINVAL;
++
++ entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE);
++
++ val = READ_ONCE(entry[0]);
++ val &= ~IVPU_MMU_CD_0_R;
++ WRITE_ONCE(entry[0], val);
++
++ if (!ivpu_is_force_snoop_enabled(vdev))
++ clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
++
++ ivpu_mmu_cmdq_write_cfgi_all(vdev);
++
++ return 0;
++}
++
+ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
+ {
++ struct ivpu_file_priv *file_priv;
++ u32 last_ssid = -1;
+ u32 *event;
+ u32 ssid;
+
+ ivpu_dbg(vdev, IRQ, "MMU event queue\n");
+
+- while ((event = ivpu_mmu_get_event(vdev)) != NULL) {
+- ivpu_mmu_dump_event(vdev, event);
+-
++ while ((event = ivpu_mmu_get_event(vdev))) {
+ ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);
++
++ if (ssid == last_ssid)
++ continue;
++
++ xa_lock(&vdev->context_xa);
++ file_priv = xa_load(&vdev->context_xa, ssid);
++ if (file_priv) {
++ if (file_priv->has_mmu_faults) {
++ event = NULL;
++ } else {
++ ivpu_mmu_disable_events(vdev, ssid);
++ file_priv->has_mmu_faults = true;
++ }
++ }
++ xa_unlock(&vdev->context_xa);
++
++ if (event)
++ ivpu_mmu_dump_event(vdev, event);
++
+ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
+ ivpu_pm_trigger_recovery(vdev, "MMU event");
+ return;
+ }
+-
+- ivpu_mmu_user_context_mark_invalid(vdev, ssid);
+ REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
+ }
+
+--- a/drivers/accel/ivpu/ivpu_mmu_context.c
++++ b/drivers/accel/ivpu/ivpu_mmu_context.c
+@@ -635,16 +635,3 @@ void ivpu_mmu_reserved_context_fini(stru
+ ivpu_mmu_cd_clear(vdev, vdev->rctx.id);
+ ivpu_mmu_context_fini(vdev, &vdev->rctx);
+ }
+-
+-void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
+-{
+- struct ivpu_file_priv *file_priv;
+-
+- xa_lock(&vdev->context_xa);
+-
+- file_priv = xa_load(&vdev->context_xa, ssid);
+- if (file_priv)
+- file_priv->has_mmu_faults = true;
+-
+- xa_unlock(&vdev->context_xa);
+-}
+--- a/drivers/accel/ivpu/ivpu_mmu_context.h
++++ b/drivers/accel/ivpu/ivpu_mmu_context.h
+@@ -37,8 +37,6 @@ void ivpu_mmu_global_context_fini(struct
+ int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev);
+ void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev);
+
+-void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid);
+-
+ int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range,
+ u64 size, struct drm_mm_node *node);
+ void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node);
--- /dev/null
+From 353b8f48390d36b39276ff6af61464ec64cd4d5c Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 7 Jan 2025 18:32:31 +0100
+Subject: accel/ivpu: Fix missing MMU events from reserved SSID
+
+From: Karol Wachowski <karol.wachowski@intel.com>
+
+commit 353b8f48390d36b39276ff6af61464ec64cd4d5c upstream.
+
+Generate recovery when fault from reserved context is detected.
+Add Abort (A) bit to reserved (1) SSID to ensure NPU also receives a fault.
+
+There is no way to create a file_priv with reserved SSID
+but it is still possible to receive MMU faults from that SSID
+as it is a default NPU HW setting. Such situation will occur if
+FW freed context related resources but still performed access to DRAM.
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-9-maciej.falkowski@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_mmu.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/accel/ivpu/ivpu_mmu.c
++++ b/drivers/accel/ivpu/ivpu_mmu.c
+@@ -725,8 +725,8 @@ static int ivpu_mmu_cdtab_entry_set(stru
+ cd[2] = 0;
+ cd[3] = 0x0000000000007444;
+
+- /* For global context generate memory fault on VPU */
+- if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID)
++ /* For global and reserved contexts generate memory fault on VPU */
++ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID || ssid == IVPU_RESERVED_CONTEXT_MMU_SSID)
+ cd[0] |= IVPU_MMU_CD_0_A;
+
+ if (valid)
+@@ -945,7 +945,8 @@ void ivpu_mmu_irq_evtq_handler(struct iv
+
+ while ((event = ivpu_mmu_get_event(vdev))) {
+ ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, *event);
+- if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
++ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID ||
++ ssid == IVPU_RESERVED_CONTEXT_MMU_SSID) {
+ ivpu_mmu_dump_event(vdev, event);
+ ivpu_pm_trigger_recovery(vdev, "MMU event");
+ return;
--- /dev/null
+From 2f5bbea1807a064a1e4c1b385c8cea4f37bb4b17 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Wed, 29 Jan 2025 13:56:33 +0100
+Subject: accel/ivpu: Fix missing MMU events if file_priv is unbound
+
+From: Karol Wachowski <karol.wachowski@intel.com>
+
+commit 2f5bbea1807a064a1e4c1b385c8cea4f37bb4b17 upstream.
+
+Move the ivpu_mmu_discard_events() function to the common portion of
+the abort work function. This ensures it is called only once, even if
+there are no faulty contexts in context_xa, to guarantee that MMU events
+are discarded and new events are not missed.
+
+Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Reviewed-by: Jeffrey Hugo <quic_jhugo@quicinc.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250129125636.1047413-4-jacek.lawrynowicz@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_job.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/accel/ivpu/ivpu_job.c
++++ b/drivers/accel/ivpu/ivpu_job.c
+@@ -369,7 +369,6 @@ void ivpu_context_abort_locked(struct iv
+ ivpu_jsm_context_release(vdev, file_priv->ctx.id);
+
+ ivpu_mmu_disable_ssid_events(vdev, file_priv->ctx.id);
+- ivpu_mmu_discard_events(vdev);
+
+ file_priv->aborted = true;
+ }
+@@ -872,6 +871,13 @@ void ivpu_context_abort_work_fn(struct w
+ }
+ mutex_unlock(&vdev->context_list_lock);
+
++ /*
++ * We will not receive new MMU event interrupts until existing events are discarded
++ * however, we want to discard these events only after aborting the faulty context
++ * to avoid generating new faults from that context
++ */
++ ivpu_mmu_discard_events(vdev);
++
+ if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ return;
+
--- /dev/null
+From 683e9fa1c885a0cffbc10b459a7eee9df92af1c1 Mon Sep 17 00:00:00 2001
+From: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Date: Tue, 1 Apr 2025 17:57:55 +0200
+Subject: accel/ivpu: Flush pending jobs of device's workqueues
+
+From: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+
+commit 683e9fa1c885a0cffbc10b459a7eee9df92af1c1 upstream.
+
+Use flush_work() instead of cancel_work_sync() for driver IRQ
+workqueues to guarantee that remaining pending work
+will be handled.
+
+This resolves two issues that were encountered where a driver was left
+in an incorrect state as the bottom-half was canceled:
+
+1. Cancelling context-abort of a job that is still executing and
+ is causing translation faults which is going to cause additional TDRs
+
+2. Cancelling bottom-half of a DCT (duty-cycle throttling) request
+ which will cause a device to not be adjusted to an external frequency
+ request.
+
+Fixes: bc3e5f48b7ee ("accel/ivpu: Use workqueue for IRQ handling")
+Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Reviewed-by: Lizhi Hou <lizhi.hou@amd.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://lore.kernel.org/r/20250401155755.4049156-1-maciej.falkowski@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_drv.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/accel/ivpu/ivpu_drv.c
++++ b/drivers/accel/ivpu/ivpu_drv.c
+@@ -420,9 +420,9 @@ void ivpu_prepare_for_reset(struct ivpu_
+ {
+ ivpu_hw_irq_disable(vdev);
+ disable_irq(vdev->irq);
+- cancel_work_sync(&vdev->irq_ipc_work);
+- cancel_work_sync(&vdev->irq_dct_work);
+- cancel_work_sync(&vdev->context_abort_work);
++ flush_work(&vdev->irq_ipc_work);
++ flush_work(&vdev->irq_dct_work);
++ flush_work(&vdev->context_abort_work);
+ ivpu_ipc_disable(vdev);
+ ivpu_mmu_disable(vdev);
+ }
--- /dev/null
+From 4480912f3f8b8a1fbb5ae12c5c547fd094ec4197 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 7 Jan 2025 18:32:30 +0100
+Subject: accel/ivpu: Move parts of MMU event IRQ handling to thread handler
+
+From: Karol Wachowski <karol.wachowski@intel.com>
+
+commit 4480912f3f8b8a1fbb5ae12c5c547fd094ec4197 upstream.
+
+To prevent looping infinitely in MMU event handler we stop
+generating new events by removing 'R' (record) bit from context
+descriptor, but to ensure this change has effect KMD has to perform
+configuration invalidation followed by sync command.
+
+Because of that move parts of the interrupt handler that can take longer
+to a thread not to block in interrupt handler for too long.
+This includes:
+ * disabling event queue for the time KMD updates MMU event queue consumer
+ to ensure proper synchronization between MMU and KMD
+
+ * removal of 'R' (record) bit from context descriptor to ensure no more
+ faults are recorded until that context is destroyed
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-8-maciej.falkowski@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_job.c | 7 ++-
+ drivers/accel/ivpu/ivpu_mmu.c | 93 +++++++++++++++++++++++++++---------------
+ drivers/accel/ivpu/ivpu_mmu.h | 2
+ 3 files changed, 69 insertions(+), 33 deletions(-)
+
+--- a/drivers/accel/ivpu/ivpu_job.c
++++ b/drivers/accel/ivpu/ivpu_job.c
+@@ -17,6 +17,7 @@
+ #include "ivpu_ipc.h"
+ #include "ivpu_job.h"
+ #include "ivpu_jsm_msg.h"
++#include "ivpu_mmu.h"
+ #include "ivpu_pm.h"
+ #include "ivpu_trace.h"
+ #include "vpu_boot_api.h"
+@@ -360,12 +361,16 @@ void ivpu_context_abort_locked(struct iv
+ struct ivpu_device *vdev = file_priv->vdev;
+
+ lockdep_assert_held(&file_priv->lock);
++ ivpu_dbg(vdev, JOB, "Context ID: %u abort\n", file_priv->ctx.id);
+
+ ivpu_cmdq_fini_all(file_priv);
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_OS)
+ ivpu_jsm_context_release(vdev, file_priv->ctx.id);
+
++ ivpu_mmu_disable_ssid_events(vdev, file_priv->ctx.id);
++ ivpu_mmu_discard_events(vdev);
++
+ file_priv->aborted = true;
+ }
+
+@@ -849,8 +854,8 @@ void ivpu_context_abort_work_fn(struct w
+ {
+ struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work);
+ struct ivpu_file_priv *file_priv;
+- unsigned long ctx_id;
+ struct ivpu_job *job;
++ unsigned long ctx_id;
+ unsigned long id;
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
+--- a/drivers/accel/ivpu/ivpu_mmu.c
++++ b/drivers/accel/ivpu/ivpu_mmu.c
+@@ -20,6 +20,12 @@
+ #define IVPU_MMU_REG_CR0 0x00200020u
+ #define IVPU_MMU_REG_CR0ACK 0x00200024u
+ #define IVPU_MMU_REG_CR0ACK_VAL_MASK GENMASK(31, 0)
++#define IVPU_MMU_REG_CR0_ATSCHK_MASK BIT(4)
++#define IVPU_MMU_REG_CR0_CMDQEN_MASK BIT(3)
++#define IVPU_MMU_REG_CR0_EVTQEN_MASK BIT(2)
++#define IVPU_MMU_REG_CR0_PRIQEN_MASK BIT(1)
++#define IVPU_MMU_REG_CR0_SMMUEN_MASK BIT(0)
++
+ #define IVPU_MMU_REG_CR1 0x00200028u
+ #define IVPU_MMU_REG_CR2 0x0020002cu
+ #define IVPU_MMU_REG_IRQ_CTRL 0x00200050u
+@@ -141,12 +147,6 @@
+ #define IVPU_MMU_IRQ_EVTQ_EN BIT(2)
+ #define IVPU_MMU_IRQ_GERROR_EN BIT(0)
+
+-#define IVPU_MMU_CR0_ATSCHK BIT(4)
+-#define IVPU_MMU_CR0_CMDQEN BIT(3)
+-#define IVPU_MMU_CR0_EVTQEN BIT(2)
+-#define IVPU_MMU_CR0_PRIQEN BIT(1)
+-#define IVPU_MMU_CR0_SMMUEN BIT(0)
+-
+ #define IVPU_MMU_CR1_TABLE_SH GENMASK(11, 10)
+ #define IVPU_MMU_CR1_TABLE_OC GENMASK(9, 8)
+ #define IVPU_MMU_CR1_TABLE_IC GENMASK(7, 6)
+@@ -596,7 +596,7 @@ static int ivpu_mmu_reset(struct ivpu_de
+ REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, 0);
+ REGV_WR32(IVPU_MMU_REG_CMDQ_CONS, 0);
+
+- val = IVPU_MMU_CR0_CMDQEN;
++ val = REG_SET_FLD(IVPU_MMU_REG_CR0, CMDQEN, 0);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+@@ -617,12 +617,12 @@ static int ivpu_mmu_reset(struct ivpu_de
+ REGV_WR32(IVPU_MMU_REG_EVTQ_PROD_SEC, 0);
+ REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, 0);
+
+- val |= IVPU_MMU_CR0_EVTQEN;
++ val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+
+- val |= IVPU_MMU_CR0_ATSCHK;
++ val = REG_SET_FLD(IVPU_MMU_REG_CR0, ATSCHK, val);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+@@ -631,7 +631,7 @@ static int ivpu_mmu_reset(struct ivpu_de
+ if (ret)
+ return ret;
+
+- val |= IVPU_MMU_CR0_SMMUEN;
++ val = REG_SET_FLD(IVPU_MMU_REG_CR0, SMMUEN, val);
+ return ivpu_mmu_reg_write_cr0(vdev, val);
+ }
+
+@@ -870,7 +870,47 @@ static u32 *ivpu_mmu_get_event(struct iv
+ return evt;
+ }
+
+-static int ivpu_mmu_disable_events(struct ivpu_device *vdev, u32 ssid)
++static int ivpu_mmu_evtq_set(struct ivpu_device *vdev, bool enable)
++{
++ u32 val = REGV_RD32(IVPU_MMU_REG_CR0);
++
++ if (enable)
++ val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
++ else
++ val = REG_CLR_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
++ REGV_WR32(IVPU_MMU_REG_CR0, val);
++
++ return REGV_POLL_FLD(IVPU_MMU_REG_CR0ACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
++}
++
++static int ivpu_mmu_evtq_enable(struct ivpu_device *vdev)
++{
++ return ivpu_mmu_evtq_set(vdev, true);
++}
++
++static int ivpu_mmu_evtq_disable(struct ivpu_device *vdev)
++{
++ return ivpu_mmu_evtq_set(vdev, false);
++}
++
++void ivpu_mmu_discard_events(struct ivpu_device *vdev)
++{
++ /*
++ * Disable event queue (stop MMU from updating the producer)
++ * to allow synchronization of consumer and producer indexes
++ */
++ ivpu_mmu_evtq_disable(vdev);
++
++ vdev->mmu->evtq.cons = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
++ REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
++ vdev->mmu->evtq.prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
++
++ ivpu_mmu_evtq_enable(vdev);
++
++ drm_WARN_ON_ONCE(&vdev->drm, vdev->mmu->evtq.cons != vdev->mmu->evtq.prod);
++}
++
++int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid)
+ {
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
+@@ -890,6 +930,7 @@ static int ivpu_mmu_disable_events(struc
+ clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
+
+ ivpu_mmu_cmdq_write_cfgi_all(vdev);
++ ivpu_mmu_cmdq_sync(vdev);
+
+ return 0;
+ }
+@@ -897,38 +938,26 @@ static int ivpu_mmu_disable_events(struc
+ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
+ {
+ struct ivpu_file_priv *file_priv;
+- u32 last_ssid = -1;
+ u32 *event;
+ u32 ssid;
+
+ ivpu_dbg(vdev, IRQ, "MMU event queue\n");
+
+ while ((event = ivpu_mmu_get_event(vdev))) {
+- ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);
+-
+- if (ssid == last_ssid)
+- continue;
++ ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, *event);
++ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
++ ivpu_mmu_dump_event(vdev, event);
++ ivpu_pm_trigger_recovery(vdev, "MMU event");
++ return;
++ }
+
+- xa_lock(&vdev->context_xa);
+ file_priv = xa_load(&vdev->context_xa, ssid);
+ if (file_priv) {
+- if (file_priv->has_mmu_faults) {
+- event = NULL;
+- } else {
+- ivpu_mmu_disable_events(vdev, ssid);
+- file_priv->has_mmu_faults = true;
++ if (!READ_ONCE(file_priv->has_mmu_faults)) {
++ ivpu_mmu_dump_event(vdev, event);
++ WRITE_ONCE(file_priv->has_mmu_faults, true);
+ }
+ }
+- xa_unlock(&vdev->context_xa);
+-
+- if (event)
+- ivpu_mmu_dump_event(vdev, event);
+-
+- if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
+- ivpu_pm_trigger_recovery(vdev, "MMU event");
+- return;
+- }
+- REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
+ }
+
+ queue_work(system_wq, &vdev->context_abort_work);
+--- a/drivers/accel/ivpu/ivpu_mmu.h
++++ b/drivers/accel/ivpu/ivpu_mmu.h
+@@ -47,5 +47,7 @@ int ivpu_mmu_invalidate_tlb(struct ivpu_
+ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev);
+ void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev);
+ void ivpu_mmu_evtq_dump(struct ivpu_device *vdev);
++void ivpu_mmu_discard_events(struct ivpu_device *vdev);
++int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid);
+
+ #endif /* __IVPU_MMU_H__ */
--- /dev/null
+From bc3e5f48b7ee021371dc37297678f7089be6ce28 Mon Sep 17 00:00:00 2001
+From: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Date: Tue, 7 Jan 2025 18:32:28 +0100
+Subject: accel/ivpu: Use workqueue for IRQ handling
+
+From: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+
+commit bc3e5f48b7ee021371dc37297678f7089be6ce28 upstream.
+
+Convert IRQ bottom half from the thread handler into workqueue.
+This increases a stability in rare scenarios where driver on
+debugging/hardening kernels processes IRQ too slow and misses
+some interrupts due to it.
+Workqueue handler also gives a very minor performance increase.
+
+Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
+Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-6-maciej.falkowski@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/accel/ivpu/ivpu_drv.c | 39 +++++++++-----------------------------
+ drivers/accel/ivpu/ivpu_drv.h | 5 +++-
+ drivers/accel/ivpu/ivpu_hw.c | 5 ----
+ drivers/accel/ivpu/ivpu_hw.h | 9 --------
+ drivers/accel/ivpu/ivpu_hw_btrs.c | 3 --
+ drivers/accel/ivpu/ivpu_ipc.c | 7 ++----
+ drivers/accel/ivpu/ivpu_ipc.h | 2 -
+ drivers/accel/ivpu/ivpu_job.c | 2 -
+ drivers/accel/ivpu/ivpu_job.h | 2 -
+ drivers/accel/ivpu/ivpu_pm.c | 3 +-
+ drivers/accel/ivpu/ivpu_pm.h | 2 -
+ 11 files changed, 24 insertions(+), 55 deletions(-)
+
+--- a/drivers/accel/ivpu/ivpu_drv.c
++++ b/drivers/accel/ivpu/ivpu_drv.c
+@@ -7,6 +7,7 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/pm_runtime.h>
++#include <linux/workqueue.h>
+ #include <generated/utsrelease.h>
+
+ #include <drm/drm_accel.h>
+@@ -419,6 +420,9 @@ void ivpu_prepare_for_reset(struct ivpu_
+ {
+ ivpu_hw_irq_disable(vdev);
+ disable_irq(vdev->irq);
++ cancel_work_sync(&vdev->irq_ipc_work);
++ cancel_work_sync(&vdev->irq_dct_work);
++ cancel_work_sync(&vdev->context_abort_work);
+ ivpu_ipc_disable(vdev);
+ ivpu_mmu_disable(vdev);
+ }
+@@ -463,31 +467,6 @@ static const struct drm_driver driver =
+ .major = 1,
+ };
+
+-static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg)
+-{
+- struct ivpu_device *vdev = arg;
+- u8 irq_src;
+-
+- if (kfifo_is_empty(&vdev->hw->irq.fifo))
+- return IRQ_NONE;
+-
+- while (kfifo_get(&vdev->hw->irq.fifo, &irq_src)) {
+- switch (irq_src) {
+- case IVPU_HW_IRQ_SRC_IPC:
+- ivpu_ipc_irq_thread_handler(vdev);
+- break;
+- case IVPU_HW_IRQ_SRC_DCT:
+- ivpu_pm_dct_irq_thread_handler(vdev);
+- break;
+- default:
+- ivpu_err_ratelimited(vdev, "Unknown IRQ source: %u\n", irq_src);
+- break;
+- }
+- }
+-
+- return IRQ_HANDLED;
+-}
+-
+ static int ivpu_irq_init(struct ivpu_device *vdev)
+ {
+ struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
+@@ -499,12 +478,16 @@ static int ivpu_irq_init(struct ivpu_dev
+ return ret;
+ }
+
++ INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
++ INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
++ INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);
++
+ ivpu_irq_handlers_init(vdev);
+
+ vdev->irq = pci_irq_vector(pdev, 0);
+
+- ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
+- ivpu_irq_thread_handler, IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
++ ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
++ IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);
+
+@@ -597,8 +580,6 @@ static int ivpu_dev_init(struct ivpu_dev
+ vdev->db_limit.min = IVPU_MIN_DB;
+ vdev->db_limit.max = IVPU_MAX_DB;
+
+- INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_thread_handler);
+-
+ ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock);
+ if (ret)
+ goto err_xa_destroy;
+--- a/drivers/accel/ivpu/ivpu_drv.h
++++ b/drivers/accel/ivpu/ivpu_drv.h
+@@ -137,12 +137,15 @@ struct ivpu_device {
+ struct mutex context_list_lock; /* Protects user context addition/removal */
+ struct xarray context_xa;
+ struct xa_limit context_xa_limit;
+- struct work_struct context_abort_work;
+
+ struct xarray db_xa;
+ struct xa_limit db_limit;
+ u32 db_next;
+
++ struct work_struct irq_ipc_work;
++ struct work_struct irq_dct_work;
++ struct work_struct context_abort_work;
++
+ struct mutex bo_list_lock; /* Protects bo_list */
+ struct list_head bo_list;
+
+--- a/drivers/accel/ivpu/ivpu_hw.c
++++ b/drivers/accel/ivpu/ivpu_hw.c
+@@ -285,8 +285,6 @@ void ivpu_hw_profiling_freq_drive(struct
+
+ void ivpu_irq_handlers_init(struct ivpu_device *vdev)
+ {
+- INIT_KFIFO(vdev->hw->irq.fifo);
+-
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_37xx;
+ else
+@@ -300,7 +298,6 @@ void ivpu_irq_handlers_init(struct ivpu_
+
+ void ivpu_hw_irq_enable(struct ivpu_device *vdev)
+ {
+- kfifo_reset(&vdev->hw->irq.fifo);
+ ivpu_hw_ip_irq_enable(vdev);
+ ivpu_hw_btrs_irq_enable(vdev);
+ }
+@@ -327,8 +324,6 @@ irqreturn_t ivpu_hw_irq_handler(int irq,
+ /* Re-enable global interrupts to re-trigger MSI for pending interrupts */
+ ivpu_hw_btrs_global_int_enable(vdev);
+
+- if (!kfifo_is_empty(&vdev->hw->irq.fifo))
+- return IRQ_WAKE_THREAD;
+ if (ip_handled || btrs_handled)
+ return IRQ_HANDLED;
+ return IRQ_NONE;
+--- a/drivers/accel/ivpu/ivpu_hw.h
++++ b/drivers/accel/ivpu/ivpu_hw.h
+@@ -6,18 +6,10 @@
+ #ifndef __IVPU_HW_H__
+ #define __IVPU_HW_H__
+
+-#include <linux/kfifo.h>
+-
+ #include "ivpu_drv.h"
+ #include "ivpu_hw_btrs.h"
+ #include "ivpu_hw_ip.h"
+
+-#define IVPU_HW_IRQ_FIFO_LENGTH 1024
+-
+-#define IVPU_HW_IRQ_SRC_IPC 1
+-#define IVPU_HW_IRQ_SRC_MMU_EVTQ 2
+-#define IVPU_HW_IRQ_SRC_DCT 3
+-
+ struct ivpu_addr_range {
+ resource_size_t start;
+ resource_size_t end;
+@@ -27,7 +19,6 @@ struct ivpu_hw_info {
+ struct {
+ bool (*btrs_irq_handler)(struct ivpu_device *vdev, int irq);
+ bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
+- DECLARE_KFIFO(fifo, u8, IVPU_HW_IRQ_FIFO_LENGTH);
+ } irq;
+ struct {
+ struct ivpu_addr_range global;
+--- a/drivers/accel/ivpu/ivpu_hw_btrs.c
++++ b/drivers/accel/ivpu/ivpu_hw_btrs.c
+@@ -666,8 +666,7 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
+ ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
+- if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_DCT))
+- ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
++ queue_work(system_wq, &vdev->irq_dct_work);
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
+--- a/drivers/accel/ivpu/ivpu_ipc.c
++++ b/drivers/accel/ivpu/ivpu_ipc.c
+@@ -460,13 +460,12 @@ void ivpu_ipc_irq_handler(struct ivpu_de
+ }
+ }
+
+- if (!list_empty(&ipc->cb_msg_list))
+- if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_IPC))
+- ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
++ queue_work(system_wq, &vdev->irq_ipc_work);
+ }
+
+-void ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev)
++void ivpu_ipc_irq_work_fn(struct work_struct *work)
+ {
++ struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work);
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_rx_msg *rx_msg, *r;
+ struct list_head cb_msg_list;
+--- a/drivers/accel/ivpu/ivpu_ipc.h
++++ b/drivers/accel/ivpu/ivpu_ipc.h
+@@ -90,7 +90,7 @@ void ivpu_ipc_disable(struct ivpu_device
+ void ivpu_ipc_reset(struct ivpu_device *vdev);
+
+ void ivpu_ipc_irq_handler(struct ivpu_device *vdev);
+-void ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev);
++void ivpu_ipc_irq_work_fn(struct work_struct *work);
+
+ void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ u32 channel, ivpu_ipc_rx_callback_t callback);
+--- a/drivers/accel/ivpu/ivpu_job.c
++++ b/drivers/accel/ivpu/ivpu_job.c
+@@ -845,7 +845,7 @@ void ivpu_job_done_consumer_fini(struct
+ ivpu_ipc_consumer_del(vdev, &vdev->job_done_consumer);
+ }
+
+-void ivpu_context_abort_thread_handler(struct work_struct *work)
++void ivpu_context_abort_work_fn(struct work_struct *work)
+ {
+ struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work);
+ struct ivpu_file_priv *file_priv;
+--- a/drivers/accel/ivpu/ivpu_job.h
++++ b/drivers/accel/ivpu/ivpu_job.h
+@@ -66,7 +66,7 @@ void ivpu_cmdq_reset_all_contexts(struct
+
+ void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
+ void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
+-void ivpu_context_abort_thread_handler(struct work_struct *work);
++void ivpu_context_abort_work_fn(struct work_struct *work);
+
+ void ivpu_jobs_abort_all(struct ivpu_device *vdev);
+
+--- a/drivers/accel/ivpu/ivpu_pm.c
++++ b/drivers/accel/ivpu/ivpu_pm.c
+@@ -464,8 +464,9 @@ int ivpu_pm_dct_disable(struct ivpu_devi
+ return 0;
+ }
+
+-void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev)
++void ivpu_pm_irq_dct_work_fn(struct work_struct *work)
+ {
++ struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_dct_work);
+ bool enable;
+ int ret;
+
+--- a/drivers/accel/ivpu/ivpu_pm.h
++++ b/drivers/accel/ivpu/ivpu_pm.h
+@@ -45,6 +45,6 @@ void ivpu_stop_job_timeout_detection(str
+ int ivpu_pm_dct_init(struct ivpu_device *vdev);
+ int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent);
+ int ivpu_pm_dct_disable(struct ivpu_device *vdev);
+-void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev);
++void ivpu_pm_irq_dct_work_fn(struct work_struct *work);
+
+ #endif /* __IVPU_PM_H__ */
--- /dev/null
+From 03552d8ac0afcc080c339faa0b726e2c0e9361cb Mon Sep 17 00:00:00 2001
+From: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
+Date: Fri, 2 May 2025 08:51:04 -0700
+Subject: drm/xe/gsc: do not flush the GSC worker from the reset path
+
+From: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
+
+commit 03552d8ac0afcc080c339faa0b726e2c0e9361cb upstream.
+
+The workqueue used for the reset worker is marked as WQ_MEM_RECLAIM,
+while the GSC one isn't (and can't be as we need to do memory
+allocations in the gsc worker). Therefore, we can't flush the latter
+from the former.
+
+The reason why we had such a flush was to avoid interrupting either
+the GSC FW load or in progress GSC proxy operations. GSC proxy
+operations fall into 2 categories:
+
+1) GSC proxy init: this only happens once immediately after GSC FW load
+ and does not support being interrupted. The only way to recover from
+ an interruption of the proxy init is to do an FLR and re-load the GSC.
+
+2) GSC proxy request: this can happen in response to a request that
+ the driver sends to the GSC. If this is interrupted, the GSC FW will
+ timeout and the driver request will be failed, but overall the GSC
+ will keep working fine.
+
+Flushing the work allowed us to avoid interruption in both cases (unless
+the hang came from the GSC engine itself, in which case we're toast
+anyway). However, a failure on a proxy request is tolerable if we're in
+a scenario where we're triggering a GT reset (i.e., something is already
+gone pretty wrong), so what we really need to avoid is interrupting
+the init flow, which we can do by polling on the register that reports
+when the proxy init is complete (as that ensure us that all the load and
+init operations have been completed).
+
+Note that during suspend we still want to do a flush of the worker to
+make sure it completes any operations involving the HW before the power
+is cut.
+
+v2: fix spelling in commit msg, rename waiter function (Julia)
+
+Fixes: dd0e89e5edc2 ("drm/xe/gsc: GSC FW load")
+Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/4830
+Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
+Cc: John Harrison <John.C.Harrison@Intel.com>
+Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
+Cc: <stable@vger.kernel.org> # v6.8+
+Reviewed-by: Julia Filipchuk <julia.filipchuk@intel.com>
+Link: https://lore.kernel.org/r/20250502155104.2201469-1-daniele.ceraolospurio@intel.com
+(cherry picked from commit 12370bfcc4f0bdf70279ec5b570eb298963422b5)
+Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/xe/xe_gsc.c | 22 ++++++++++++++++++++++
+ drivers/gpu/drm/xe/xe_gsc.h | 1 +
+ drivers/gpu/drm/xe/xe_gsc_proxy.c | 11 +++++++++++
+ drivers/gpu/drm/xe/xe_gsc_proxy.h | 1 +
+ drivers/gpu/drm/xe/xe_gt.c | 2 +-
+ drivers/gpu/drm/xe/xe_uc.c | 8 +++++++-
+ drivers/gpu/drm/xe/xe_uc.h | 1 +
+ 7 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/xe/xe_gsc.c
++++ b/drivers/gpu/drm/xe/xe_gsc.c
+@@ -564,6 +564,28 @@ void xe_gsc_remove(struct xe_gsc *gsc)
+ xe_gsc_proxy_remove(gsc);
+ }
+
++void xe_gsc_stop_prepare(struct xe_gsc *gsc)
++{
++ struct xe_gt *gt = gsc_to_gt(gsc);
++ int ret;
++
++ if (!xe_uc_fw_is_loadable(&gsc->fw) || xe_uc_fw_is_in_error_state(&gsc->fw))
++ return;
++
++ xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GSC);
++
++ /*
++ * If the GSC FW load or the proxy init are interrupted, the only way
++ * to recover it is to do an FLR and reload the GSC from scratch.
++ * Therefore, let's wait for the init to complete before stopping
++ * operations. The proxy init is the last step, so we can just wait on
++ * that
++ */
++ ret = xe_gsc_wait_for_proxy_init_done(gsc);
++ if (ret)
++ xe_gt_err(gt, "failed to wait for GSC init completion before uc stop\n");
++}
++
+ /*
+ * wa_14015076503: if the GSC FW is loaded, we need to alert it before doing a
+ * GSC engine reset by writing a notification bit in the GS1 register and then
+--- a/drivers/gpu/drm/xe/xe_gsc.h
++++ b/drivers/gpu/drm/xe/xe_gsc.h
+@@ -16,6 +16,7 @@ struct xe_hw_engine;
+ int xe_gsc_init(struct xe_gsc *gsc);
+ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc);
+ void xe_gsc_wait_for_worker_completion(struct xe_gsc *gsc);
++void xe_gsc_stop_prepare(struct xe_gsc *gsc);
+ void xe_gsc_load_start(struct xe_gsc *gsc);
+ void xe_gsc_remove(struct xe_gsc *gsc);
+ void xe_gsc_hwe_irq_handler(struct xe_hw_engine *hwe, u16 intr_vec);
+--- a/drivers/gpu/drm/xe/xe_gsc_proxy.c
++++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
+@@ -71,6 +71,17 @@ bool xe_gsc_proxy_init_done(struct xe_gs
+ HECI1_FWSTS1_PROXY_STATE_NORMAL;
+ }
+
++int xe_gsc_wait_for_proxy_init_done(struct xe_gsc *gsc)
++{
++ struct xe_gt *gt = gsc_to_gt(gsc);
++
++ /* Proxy init can take up to 500ms, so wait double that for safety */
++ return xe_mmio_wait32(>->mmio, HECI_FWSTS1(MTL_GSC_HECI1_BASE),
++ HECI1_FWSTS1_CURRENT_STATE,
++ HECI1_FWSTS1_PROXY_STATE_NORMAL,
++ USEC_PER_SEC, NULL, false);
++}
++
+ static void __gsc_proxy_irq_rmw(struct xe_gsc *gsc, u32 clr, u32 set)
+ {
+ struct xe_gt *gt = gsc_to_gt(gsc);
+--- a/drivers/gpu/drm/xe/xe_gsc_proxy.h
++++ b/drivers/gpu/drm/xe/xe_gsc_proxy.h
+@@ -13,6 +13,7 @@ struct xe_gsc;
+ int xe_gsc_proxy_init(struct xe_gsc *gsc);
+ bool xe_gsc_proxy_init_done(struct xe_gsc *gsc);
+ void xe_gsc_proxy_remove(struct xe_gsc *gsc);
++int xe_gsc_wait_for_proxy_init_done(struct xe_gsc *gsc);
+ int xe_gsc_proxy_start(struct xe_gsc *gsc);
+
+ int xe_gsc_proxy_request_handler(struct xe_gsc *gsc);
+--- a/drivers/gpu/drm/xe/xe_gt.c
++++ b/drivers/gpu/drm/xe/xe_gt.c
+@@ -862,7 +862,7 @@ void xe_gt_suspend_prepare(struct xe_gt
+
+ fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+
+- xe_uc_stop_prepare(>->uc);
++ xe_uc_suspend_prepare(>->uc);
+
+ xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ }
+--- a/drivers/gpu/drm/xe/xe_uc.c
++++ b/drivers/gpu/drm/xe/xe_uc.c
+@@ -241,7 +241,7 @@ void xe_uc_gucrc_disable(struct xe_uc *u
+
+ void xe_uc_stop_prepare(struct xe_uc *uc)
+ {
+- xe_gsc_wait_for_worker_completion(&uc->gsc);
++ xe_gsc_stop_prepare(&uc->gsc);
+ xe_guc_stop_prepare(&uc->guc);
+ }
+
+@@ -275,6 +275,12 @@ again:
+ goto again;
+ }
+
++void xe_uc_suspend_prepare(struct xe_uc *uc)
++{
++ xe_gsc_wait_for_worker_completion(&uc->gsc);
++ xe_guc_stop_prepare(&uc->guc);
++}
++
+ int xe_uc_suspend(struct xe_uc *uc)
+ {
+ /* GuC submission not enabled, nothing to do */
+--- a/drivers/gpu/drm/xe/xe_uc.h
++++ b/drivers/gpu/drm/xe/xe_uc.h
+@@ -18,6 +18,7 @@ int xe_uc_reset_prepare(struct xe_uc *uc
+ void xe_uc_stop_prepare(struct xe_uc *uc);
+ void xe_uc_stop(struct xe_uc *uc);
+ int xe_uc_start(struct xe_uc *uc);
++void xe_uc_suspend_prepare(struct xe_uc *uc);
+ int xe_uc_suspend(struct xe_uc *uc);
+ int xe_uc_sanitize_reset(struct xe_uc *uc);
+ void xe_uc_remove(struct xe_uc *uc);
--- /dev/null
+From fefc075182275057ce607effaa3daa9e6e3bdc73 Mon Sep 17 00:00:00 2001
+From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
+Date: Tue, 6 May 2025 16:32:07 +0300
+Subject: mm/page_alloc: fix race condition in unaccepted memory handling
+
+From: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+
+commit fefc075182275057ce607effaa3daa9e6e3bdc73 upstream.
+
+The page allocator tracks the number of zones that have unaccepted memory
+using static_branch_enc/dec() and uses that static branch in hot paths to
+determine if it needs to deal with unaccepted memory.
+
+Borislav and Thomas pointed out that the tracking is racy: operations on
+static_branch are not serialized against adding/removing unaccepted pages
+to/from the zone.
+
+Sanity checks inside static_branch machinery detects it:
+
+WARNING: CPU: 0 PID: 10 at kernel/jump_label.c:276 __static_key_slow_dec_cpuslocked+0x8e/0xa0
+
+The comment around the WARN() explains the problem:
+
+ /*
+ * Warn about the '-1' case though; since that means a
+ * decrement is concurrent with a first (0->1) increment. IOW
+ * people are trying to disable something that wasn't yet fully
+ * enabled. This suggests an ordering problem on the user side.
+ */
+
+The effect of this static_branch optimization is only visible on
+microbenchmark.
+
+Instead of adding more complexity around it, remove it altogether.
+
+Link: https://lkml.kernel.org/r/20250506133207.1009676-1-kirill.shutemov@linux.intel.com
+Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+Fixes: dcdfdd40fa82 ("mm: Add support for unaccepted memory")
+Link: https://lore.kernel.org/all/20250506092445.GBaBnVXXyvnazly6iF@fat_crate.local
+Reported-by: Borislav Petkov <bp@alien8.de>
+Tested-by: Borislav Petkov (AMD) <bp@alien8.de>
+Reported-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Brendan Jackman <jackmanb@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: <stable@vger.kernel.org> [6.5+]
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/page_alloc.c | 23 -----------------------
+ 1 file changed, 23 deletions(-)
+
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -6951,9 +6951,6 @@ bool has_managed_dma(void)
+
+ #ifdef CONFIG_UNACCEPTED_MEMORY
+
+-/* Counts number of zones with unaccepted pages. */
+-static DEFINE_STATIC_KEY_FALSE(zones_with_unaccepted_pages);
+-
+ static bool lazy_accept = true;
+
+ static int __init accept_memory_parse(char *p)
+@@ -6980,11 +6977,7 @@ static bool page_contains_unaccepted(str
+ static void __accept_page(struct zone *zone, unsigned long *flags,
+ struct page *page)
+ {
+- bool last;
+-
+ list_del(&page->lru);
+- last = list_empty(&zone->unaccepted_pages);
+-
+ account_freepages(zone, -MAX_ORDER_NR_PAGES, MIGRATE_MOVABLE);
+ __mod_zone_page_state(zone, NR_UNACCEPTED, -MAX_ORDER_NR_PAGES);
+ __ClearPageUnaccepted(page);
+@@ -6993,9 +6986,6 @@ static void __accept_page(struct zone *z
+ accept_memory(page_to_phys(page), PAGE_SIZE << MAX_PAGE_ORDER);
+
+ __free_pages_ok(page, MAX_PAGE_ORDER, FPI_TO_TAIL);
+-
+- if (last)
+- static_branch_dec(&zones_with_unaccepted_pages);
+ }
+
+ void accept_page(struct page *page)
+@@ -7032,19 +7022,11 @@ static bool try_to_accept_memory_one(str
+ return true;
+ }
+
+-static inline bool has_unaccepted_memory(void)
+-{
+- return static_branch_unlikely(&zones_with_unaccepted_pages);
+-}
+-
+ static bool cond_accept_memory(struct zone *zone, unsigned int order)
+ {
+ long to_accept, wmark;
+ bool ret = false;
+
+- if (!has_unaccepted_memory())
+- return false;
+-
+ if (list_empty(&zone->unaccepted_pages))
+ return false;
+
+@@ -7078,22 +7060,17 @@ static bool __free_unaccepted(struct pag
+ {
+ struct zone *zone = page_zone(page);
+ unsigned long flags;
+- bool first = false;
+
+ if (!lazy_accept)
+ return false;
+
+ spin_lock_irqsave(&zone->lock, flags);
+- first = list_empty(&zone->unaccepted_pages);
+ list_add_tail(&page->lru, &zone->unaccepted_pages);
+ account_freepages(zone, MAX_ORDER_NR_PAGES, MIGRATE_MOVABLE);
+ __mod_zone_page_state(zone, NR_UNACCEPTED, MAX_ORDER_NR_PAGES);
+ __SetPageUnaccepted(page);
+ spin_unlock_irqrestore(&zone->lock, flags);
+
+- if (first)
+- static_branch_inc(&zones_with_unaccepted_pages);
+-
+ return true;
+ }
+
dmaengine-idxd-fix-memory-leak-in-error-handling-path-of-idxd_alloc.patch
dmaengine-idxd-fix-memory-leak-in-error-handling-path-of-idxd_pci_probe.patch
dmaengine-idxd-refactor-remove-call-with-idxd_cleanup-helper.patch
+accel-ivpu-use-workqueue-for-irq-handling.patch
+accel-ivpu-dump-only-first-mmu-fault-from-single-context.patch
+accel-ivpu-move-parts-of-mmu-event-irq-handling-to-thread-handler.patch
+accel-ivpu-fix-missing-mmu-events-from-reserved-ssid.patch
+accel-ivpu-fix-missing-mmu-events-if-file_priv-is-unbound.patch
+accel-ivpu-flush-pending-jobs-of-device-s-workqueues.patch
+drm-xe-gsc-do-not-flush-the-gsc-worker-from-the-reset-path.patch
+mm-page_alloc-fix-race-condition-in-unaccepted-memory-handling.patch