From e415915c5258ec7da3d783fba5e9983d03a76d07 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 8 Oct 2026 01:22:44 +0300 Subject: [PATCH 01/31] Revert "drm/virtio: Add pixel blend mode property to cursor plane" The cursor blending mode was added twice because the same change was applied by accident from different patches. Revert the second patch. Reported-by: Marek Szyprowski Link: https://lore.kernel.org/dri-devel/ce84d3d2-e78b-4939-aecf-9ef7267bd42a@samsung.com/ Fixes: 6947b78df4d2 ("drm/virtio: Add pixel blend mode property to cursor plane") Signed-off-by: Dmitry Osipenko Reviewed-by: Marek Szyprowski Link: https://patch.msgid.link/20261007222244.295186-1-dmitry.osipenko@collabora.com --- drivers/gpu/drm/virtio/virtgpu_plane.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index b422eba42a5f..640815af4098 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -589,7 +589,6 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, struct drm_plane *plane; const uint32_t *formats; int nformats; - int ret; if (type == DRM_PLANE_TYPE_CURSOR) { formats = virtio_gpu_cursor_formats; @@ -615,17 +614,5 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI)); - if (type == DRM_PLANE_TYPE_CURSOR) { - /* - * The cursor plane exposes a format with an alpha channel, - * which requires a blend mode property. The host blends - * premultiplied alpha, matching the property's default. - */ - ret = drm_plane_create_blend_mode_property(plane, - BIT(DRM_MODE_BLEND_PREMULTI)); - if (ret) - return ERR_PTR(ret); - } - return plane; } From 3f57b34485dc01e5a2db60a89a6ae5439a897e53 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 2 Oct 2026 10:36:43 -0300 Subject: [PATCH 02/31] drm/vc4: hdmi: Move packet RAM enable to PM runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audio can be triggered by the HPD handler and start up before video has been restored, calling vc4_hdmi_write_infoframe() for the audio infoframe. vc4_hdmi_write_infoframe() needs Packet RAM to be on to store the infoframe, however, if the video hasn't been enabled, vc4_hdmi_encoder_post_crtc_enable() hasn't been called and therefore, the Packet RAM can be disabled by the time vc4_hdmi_write_infoframe() executes, causing the following splat: [ 201.570266] ------------[ cut here ]------------ [ 201.570293] Packet RAM has to be on to store the packet. [ 201.570367] WARNING: drivers/gpu/drm/vc4/vc4_hdmi.c:683 at vc4_hdmi_write_infoframe+0x404/0x494 [vc4], CPU#0: wireplumber/1606 [...] [ 201.658650] Hardware name: Raspberry Pi 4 Model B Rev 1.5 (DT) [...] [ 201.758499] Call trace: [ 201.760967] vc4_hdmi_write_infoframe+0x404/0x494 [vc4] (P) [ 201.766607] vc4_hdmi_write_audio_infoframe+0x1c/0x28 [vc4] [ 201.772247] write_infoframe+0xbc/0x140 [drm_display_helper] [ 201.777975] drm_atomic_helper_connector_hdmi_update_audio_infoframe+0x80/0xdc [drm_display_helper] [ 201.787140] vc4_hdmi_audio_prepare+0x4ac/0x570 [vc4] [ 201.792252] drm_connector_hdmi_audio_prepare+0x24/0x30 [drm_display_helper] [ 201.799390] hdmi_codec_prepare+0x100/0x160 [snd_soc_hdmi_codec] [ 201.805471] snd_soc_pcm_dai_prepare+0xd4/0x100 [snd_soc_core] [ 201.811375] __soc_pcm_prepare+0x60/0x12c [snd_soc_core] [ 201.816752] soc_pcm_prepare+0x34/0x58 [snd_soc_core] [ 201.821863] snd_pcm_do_prepare+0x70/0x8c [snd_pcm] [ 201.826798] snd_pcm_action_nonatomic+0xe4/0x174 [snd_pcm] [ 201.832350] snd_pcm_prepare+0xc8/0xd8 [snd_pcm] [ 201.837021] snd_pcm_common_ioctl+0x458/0x614 [snd_pcm] [ 201.842308] snd_pcm_ioctl+0x30/0x48 [snd_pcm] [ 201.846802] __arm64_sys_ioctl+0x88/0xd8 [ 201.850769] invoke_syscall+0x48/0xf4 [ 201.854470] el0_svc_common+0x80/0xd8 [ 201.858171] do_el0_svc+0x1c/0x28 [ 201.861520] el0_svc+0x3c/0xb0 [ 201.864605] el0t_64_sync_handler+0x68/0xdc [ 201.868834] el0t_64_sync+0x198/0x19c [ 201.872536] ---[ end trace 0000000000000000 ]--- Considering that Packet RAM enable is essentially a power management configuration, move its enabling/disabling to the Runtime PM hooks. Note that post_crtc_disable() still stops all infoframes, but keeps Packet RAM enabled, leaving it to be disabled by runtime suspend. Link: https://github.com/raspberrypi/linux/issues/7069 Fixes: b47a72f30768 ("drm/vc4: hdmi: Switch to HDMI connector") Signed-off-by: Dave Stevenson Acked-by: Maxime Ripard Link: https://patch.msgid.link/20261002-vc4-hdmi-packet-ram-fix-v1-1-3d73148096c9@igalia.com Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_hdmi.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 17c8635c5afa..d4672aa81d85 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -894,7 +894,7 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); - HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); + HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE); HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB); @@ -1663,9 +1663,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, WARN_ONCE(ret, "Timeout waiting for " "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n"); } else { - HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, - HDMI_READ(HDMI_RAM_PACKET_CONFIG) & - ~(VC4_HDMI_RAM_PACKET_ENABLE)); HDMI_WRITE(HDMI_SCHEDULER_CONTROL, HDMI_READ(HDMI_SCHEDULER_CONTROL) & ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); @@ -1684,9 +1681,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)); - HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, - VC4_HDMI_RAM_PACKET_ENABLE); - spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); vc4_hdmi->packet_ram_enabled = true; @@ -3143,6 +3137,11 @@ static int vc5_hdmi_init_resources(struct drm_device *drm, static int vc4_hdmi_runtime_suspend(struct device *dev) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); clk_disable_unprepare(vc4_hdmi->audio_clock); clk_disable_unprepare(vc4_hdmi->hsm_clock); @@ -3153,9 +3152,8 @@ static int vc4_hdmi_runtime_suspend(struct device *dev) static int vc4_hdmi_runtime_resume(struct device *dev) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); - unsigned long __maybe_unused flags; + unsigned long rate, flags; u32 __maybe_unused value; - unsigned long rate; int ret; ret = clk_prepare_enable(vc4_hdmi->hsm_clock); @@ -3201,6 +3199,10 @@ static int vc4_hdmi_runtime_resume(struct device *dev) } #endif + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + return 0; err_disable_clk: From ffbfd3f98f4393e91dffce3a46ccb7cf5329cf2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Fri, 9 Oct 2026 09:00:53 +0200 Subject: [PATCH 03/31] drm/xe: Don't transfer shrinker pages for pinned bos on purgeable state change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pinned bos have their pages removed from the shrinker's shrinkable / purgeable counts on pin, and added back on unpin according to the bo's purgeable state at that time. However, xe_bo_set_purgeable_shrinker() unconditionally transfers pages between the shrinkable and purgeable counts. If the purgeable state of a pinned bo changes, for example by userspace madvising DONTNEED on a VMA of a bo that is also pinned as a scanout buffer, this underflows one count and, after unpin, leaves the other one permanently inflated. Only update the purgeable flag for pinned bos, and leave the page accounting to unpin. Fixes: 536a2ead3a8f ("drm/xe/bo: Add purgeable shrinker state helpers") Cc: Arvind Yadav Cc: Matthew Brost Cc: Himal Prasad Ghimiray Cc: Rodrigo Vivi Cc: Matthew Auld Cc: intel-xe@lists.freedesktop.org Cc: # v7.1+ Assisted-by: LLM Signed-off-by: Thomas Hellström Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20261009070053.7790-1-thomas.hellstrom@linux.intel.com (cherry picked from commit afe6bc012034123fd838328668e609bf57e46bd3) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_bo.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 7b95ce0445a9..d7849c7fb07e 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -944,6 +944,8 @@ static int xe_bo_move_notify(struct xe_bo *bo, * * Transfers pages between shrinkable and purgeable buckets when the BO * purgeable state changes. Called automatically from xe_bo_set_purgeable_state(). + * For pinned BOs, whose pages are not accounted, only the purgeable flag is + * updated, so that the correct bucket is used when the BO is unpinned. */ static void xe_bo_set_purgeable_shrinker(struct xe_bo *bo, enum xe_madv_purgeable_state new_state) @@ -960,7 +962,8 @@ static void xe_bo_set_purgeable_shrinker(struct xe_bo *bo, return; xe_tt = container_of(tt, struct xe_ttm_tt, ttm); - tt_pages = tt->num_pages; + /* Pinned bos are not accounted. Only update the purgeable flag. */ + tt_pages = xe_bo_is_pinned(bo) ? 0 : tt->num_pages; if (!xe_tt->purgeable && new_state == XE_MADV_PURGEABLE_DONTNEED) { xe_tt->purgeable = true; From 7afc36c92a2812de36ec2e15a6f281fca081beab Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Sat, 10 Oct 2026 21:46:48 +0800 Subject: [PATCH 04/31] MAINTAINERS: update my email address Switch my contact address to andyshrk@163.com Signed-off-by: Andy Yan Reviewed-by: Sebastian Reichel Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20261010134705.2131-1-andyshrk@163.com --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c2414447892c..24ed770c1c19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8573,7 +8573,7 @@ F: Documentation/devicetree/bindings/display/panel/synaptics,r63353.yaml F: drivers/gpu/drm/panel/panel-synaptics-r63353.c DRM DRIVER FOR SYNOPSYS DESIGNWARE DISPLAYPORT BRIDGE -M: Andy Yan +M: Andy Yan S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml @@ -8926,7 +8926,7 @@ F: include/linux/platform_data/shmob_drm.h DRM DRIVERS FOR ROCKCHIP M: Sandy Huang M: Heiko Stübner -M: Andy Yan +M: Andy Yan L: dri-devel@lists.freedesktop.org L: linux-rockchip@lists.infradead.org S: Maintained @@ -12880,7 +12880,7 @@ S: Maintained F: drivers/power/supply/ip5xxx_power.c INNOSILICON HDMI BRIDGE DRIVER -M: Andy Yan +M: Andy Yan L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git From 8240e19d329e9e1502fc96608d8afe6861e9df90 Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Thu, 8 Oct 2026 21:10:10 -0400 Subject: [PATCH 05/31] accel/amdxdna: Fix Kconfig invalid configuration The amdxdna has a soft dependency on the amd_pmf module. It will use an exported function if it is available and fall back to another function if it is not enabled. amdxdna and amd_pmf are tridstate modules supporting both built [y] and modularized [m] in the .config file. Since the amdxdna decides to use the amd_pmf if it is either a module or built in (see drivers/accel/amdxdna/aie2_pci.h), then there is an issue if they are not compiled either both as modules or both as built-in. Adding the optional dependency to Kconfig does not allow the configuration from being selected. Fixes: f66d6cc6891e4 ("accel/amdxdna: Support sensors for column utilization") Signed-off-by: Liam R. Howlett Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20261009011010.3521631-1-liam@infradead.org --- drivers/accel/amdxdna/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/Kconfig b/drivers/accel/amdxdna/Kconfig index f39d7a87296c..eddff490b4f7 100644 --- a/drivers/accel/amdxdna/Kconfig +++ b/drivers/accel/amdxdna/Kconfig @@ -6,6 +6,7 @@ config DRM_ACCEL_AMDXDNA depends on DRM_ACCEL depends on PCI && HAS_IOMEM depends on X86_64 + depends on AMD_PMF || !AMD_PMF select DRM_SCHED select DRM_GEM_SHMEM_HELPER select FW_LOADER From 4ac852419f0d317c7bd8db5b8acb3cc78be7fe33 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 14:28:45 +0800 Subject: [PATCH 06/31] accel/rocket: clear rdev on device init failure rocket_probe() stores the return value from rocket_device_init() in the file-scoped rdev pointer before checking whether it is an error pointer. If rocket_device_init() fails, rdev is left as an ERR_PTR. The rdev pointer is also used as the marker for whether the shared rocket device has already been initialized. A later core probe can therefore see a non-NULL rdev, skip rocket_device_init(), and dereference the error pointer when reading rdev->num_cores. Save the error code and clear rdev before returning from the rocket_device_init() failure path. Fixes: ed98261b4168 ("accel/rocket: Add a new driver for Rockchip's NPU") Signed-off-by: Guangshuo Li Reviewed-by: Igor Paunovic Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260708062845.716487-1-lgs201920130244@gmail.com --- drivers/accel/rocket/rocket_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c index 8bbbce594883..67e7f543fe4c 100644 --- a/drivers/accel/rocket/rocket_drv.c +++ b/drivers/accel/rocket/rocket_drv.c @@ -165,8 +165,11 @@ static int rocket_probe(struct platform_device *pdev) /* First core probing, initialize DRM device. */ rdev = rocket_device_init(drm_dev, &rocket_drm_driver); if (IS_ERR(rdev)) { + int err = PTR_ERR(rdev); + dev_err(&pdev->dev, "failed to initialize rocket device\n"); - return PTR_ERR(rdev); + rdev = NULL; + return err; } } From 6eff1d69dae808a1518aee5a58d0853a8db3f1be Mon Sep 17 00:00:00 2001 From: Igor Paunovic Date: Fri, 4 Sep 2026 14:59:36 +0200 Subject: [PATCH 07/31] accel/rocket: search every core slot when a core is removed rocket_remove() decrements rdev->num_cores for each core it removes, while find_core_for_dev() searches slots 0 to num_cores - 1. Unbinding the cores in the order they were bound therefore loses the last one: by the time it is removed the search range has already shrunk past its slot, so find_core_for_dev() returns -1 and rocket_remove() gives up without doing anything. num_cores never reaches zero, rocket_device_fini() never runs, and the file-scoped rdev keeps pointing at a device that is going away. Binding the cores again starts from that stale count, because rocket_probe() takes rdev->num_cores as the slot to fill. On an RK3588, which describes three cores, the second round lands on slots 1, 2 and 3 while rdev->cores was allocated with room for three: rocket fdab0000.npu: Rockchip NPU core 1 version: 1179210309 rocket fdac0000.npu: Rockchip NPU core 2 version: 1179210309 rocket fdad0000.npu: Rockchip NPU core 3 version: 1179210309 The write to rdev->cores[3] is past the end of the array. Nothing in tree reads the core array often enough to notice, so the overrun is silent today. It turned up while testing a devfreq series on top of this, where a worker walks every core a few times a second, and UBSAN caught the first bool it read out of the overrun entry: UBSAN: invalid-load in drivers/accel/rocket/rocket_devfreq.c:47:10 load of value 5 is not a valid value for type '_Bool' Workqueue: devfreq_wq devfreq_monitor Record how many slots were allocated and search all of them. Every core is then found on removal, num_cores reaches zero, the device is torn down and a later bind starts from a clean rdev. This does not make unbinding a single core out of several work. probe still takes num_cores as the slot to fill, so rebinding one core while its siblings stay bound would write over a slot that is already in use, and rocket_open() still reaches for cores[0] whether or not anything is there. Both of those want more thought than a fix should carry. Found by unbinding and rebinding all three cores on an Orange Pi 5 Plus. With this applied, 25 unbind/rebind rounds and 5 module unload/reload rounds run clean there: the cores land in slots 0, 1 and 2 every time, whichever order they are bound in, and the shared supply goes back to a single user after each round. Fixes: ed98261b4168 ("accel/rocket: Add a new driver for Rockchip's NPU") Cc: stable@vger.kernel.org Signed-off-by: Igor Paunovic Assisted-by: LLM sparse checkpatch Tested-by: Jiaxing Hu # RK3576, two cores Tested-by: Sidong Yang # RK3588, 3 cores Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260904125936.26234-1-royalnet026@gmail.com --- drivers/accel/rocket/rocket_device.c | 2 ++ drivers/accel/rocket/rocket_device.h | 1 + drivers/accel/rocket/rocket_drv.c | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c index 46e6ee1e72c5..efd004194c1a 100644 --- a/drivers/accel/rocket/rocket_device.c +++ b/drivers/accel/rocket/rocket_device.c @@ -31,6 +31,8 @@ struct rocket_device *rocket_device_init(struct platform_device *pdev, if (of_device_is_available(core_node)) num_cores++; + rdev->max_cores = num_cores; + rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL); if (!rdev->cores) return ERR_PTR(-ENOMEM); diff --git a/drivers/accel/rocket/rocket_device.h b/drivers/accel/rocket/rocket_device.h index ce662abc01d3..c62d56701069 100644 --- a/drivers/accel/rocket/rocket_device.h +++ b/drivers/accel/rocket/rocket_device.h @@ -19,6 +19,7 @@ struct rocket_device { struct rocket_core *cores; unsigned int num_cores; + unsigned int max_cores; }; struct rocket_device *rocket_device_init(struct platform_device *pdev, diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c index 67e7f543fe4c..5d7805c41dc3 100644 --- a/drivers/accel/rocket/rocket_drv.c +++ b/drivers/accel/rocket/rocket_drv.c @@ -226,7 +226,7 @@ static int find_core_for_dev(struct device *dev) { struct rocket_device *rdev = dev_get_drvdata(dev); - for (unsigned int core = 0; core < rdev->num_cores; core++) { + for (unsigned int core = 0; core < rdev->max_cores; core++) { if (dev == rdev->cores[core].dev) return core; } From 89c2c777df17eb0201c8e22afabbf3691f2a28fe Mon Sep 17 00:00:00 2001 From: Igor Paunovic Date: Sat, 5 Sep 2026 17:04:32 +0200 Subject: [PATCH 08/31] accel/rocket: search every core slot when looking up a scheduler sched_to_core() walks rdev->cores[] up to rdev->num_cores, and rocket_remove() decrements num_cores for every core it removes. Unbind a core that is not the last one and the cores behind it fall outside the search, so sched_to_core() returns NULL for a core that is still bound and still running jobs. Neither caller checks the result: rocket_job_run(): rocket_fence_create(core), core->dev rocket_job_timedout(): dev_err(core->dev, "NPU job timed out") Unbinding the middle core of the three on an RK3588 while three clients are submitting to all of them faults twice, once from the surviving core's job queue and once from its reset work: KASAN: null-ptr-deref in range [0x0000000000000220-0x0000000000000227] Workqueue: fdad0000.npu drm_sched_run_job_work [gpu_sched] pc : rocket_job_run+0x234/0x838 [rocket] Call trace: rocket_job_run+0x234/0x838 [rocket] drm_sched_run_job_work+0x2cc/0xad8 [gpu_sched] process_one_work+0x640/0x14f0 KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: rocket-reset-2 drm_sched_job_timedout [gpu_sched] pc : rocket_job_timedout+0xf0/0x1e0 [rocket] Call trace: rocket_job_timedout+0xf0/0x1e0 [rocket] drm_sched_job_timedout+0x188/0x6a0 [gpu_sched] Both are the third core: the workqueue names are its device and its core->index, and it was left at slot 2 while num_cores had dropped to 2. Search all the slots that were allocated, the way find_core_for_dev() now does. A core that is still bound is then found, and the two callers get the pointer they already assume they have. This does not make unbinding one core out of several safe. An open client keeps an entity pointing at the scheduler of the core that went away: drm_sched reports it as not ready for every job that lands on it, and the client waits in dma_fence_default_wait for a fence that will never signal. Stopping the NULL dereference is what belongs in a fix; the rest wants more thought. Reported-by: Sidong Yang Closes: https://lore.kernel.org/dri-devel/apwUewaRnoTNXHCt@rock-5b-plus/ Fixes: 0810d5ad88a1 ("accel/rocket: Add job submission IOCTL") Cc: stable@vger.kernel.org Signed-off-by: Igor Paunovic Assisted-by: LLM sparse checkpatch Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260905150432.7477-1-royalnet026@gmail.com --- drivers/accel/rocket/rocket_job.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index f40435505818..4bc4f9c8ee40 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -283,7 +283,7 @@ static struct rocket_core *sched_to_core(struct rocket_device *rdev, { unsigned int core; - for (core = 0; core < rdev->num_cores; core++) { + for (core = 0; core < rdev->max_cores; core++) { if (&rdev->cores[core].sched == sched) return &rdev->cores[core]; } From 8642201f02e11eeb4ebcbccfa214cdf88318b06b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 13 Oct 2026 12:57:56 +0200 Subject: [PATCH 09/31] MAINTAINERS: Add myself as reviewer for the Rocket NPU driver While I definitly do not feel qualified (yet?) to tackle the changes to deeply IP-related parts, I can at least help Tomeu with the more basic changes. Reviewed-by: Tomeu Vizoso Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20261013105757.3924282-1-heiko@sntech.de --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 24ed770c1c19..2c3e027b16db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8011,6 +8011,7 @@ F: include/uapi/drm/ivpu_accel.h DRM ACCEL DRIVER FOR ROCKCHIP NPU M: Tomeu Vizoso +R: Heiko Stuebner L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git From 5e0ed1108504dae408e8cfaabf7ee5f7bb180bca Mon Sep 17 00:00:00 2001 From: Jianfeng Liu Date: Sat, 26 Sep 2026 10:20:17 +0800 Subject: [PATCH 10/31] Revert "dma-buf: Make DMABUF_DEBUG default to y on DEBUG_KERNEL kernels" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 143755bdabaa96776c24f878014608e9cb44f930. That commit fixed a dangling reference in the DMABUF_DEBUG default and thereby enabled the option - and with it the page-stripping sg_table wrapper that dma_buf_map_attachment() hands to importers - on every kernel with DEBUG_KERNEL=y, i.e. virtually every distro kernel. drm/msm is broken by the wrapper. Both of msm's map paths consume sg->length and sg_phys() of the attachment sg_table: msm_iommu_pagetable_map() for the per-process GPU pagetables, and iommu_map_sg() (via iommu_map_sgtable()) for scanout. The wrapper zeroes sg->length and strips the page pointers, so mappings of imported dma-bufs silently map nothing, and userspace observes arm-smmu translation faults from UCHE, e.g. during hardware video decode (clapper, chromium) on Adreno systems: gpu fault: ttbr0=000000088a889000 iova=000000010741c000 dir=READ type=TRANSLATION source=UCHE Bisected on a Snapdragon X1E78100 laptop as v7.3-rc3 good, v7.3-rc4 bad, culprit 143755bdabaa9. Switching msm to sg_dma_address()/sg_dma_len() is not a trivial fix either: those fields are only valid for sg_tables that msm has dma-mapped itself, which native non-MSM_BO_WC objects' sg_tables are not, so the conversion needs more work. The msm maintainer has therefore requested restoring the previous default for v7.3, to be revisited once msm no longer consumes struct page and sg->length of imported sg_tables. Link: https://lore.kernel.org/linux-arm-msm/20260923074256.9357-1-liujianfeng1994@gmail.com/ Suggested-by: Rob Clark Cc: Christian König Cc: Sumit Semwal Cc: Karl Mehltretter Signed-off-by: Jianfeng Liu Signed-off-by: Sumit Semwal [sumits: as discussed on https://lore.kernel.org/all/CAO_48GEh1p52pA_-NE=rN=_NQ=oAXpeHgHGVZCH6uPnFq7G1gQ@mail.gmail.com/ applying this revert for 7.3 unblock] Link: https://patch.msgid.link/20260926022026.10539-1-liujianfeng1994@gmail.com --- drivers/dma-buf/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index e4f078a326a4..7efc0f0d0712 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -43,7 +43,7 @@ config UDMABUF config DMABUF_DEBUG bool "DMA-BUF debug checks" depends on DMA_SHARED_BUFFER - default y if DEBUG_KERNEL + default y if DEBUG help This option enables additional checks for DMA-BUF importers and exporters. Specifically it validates that importers do not peek at the From 448e76cbdc6652ccd3ba337b7a6e1f028859f2ed Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Mon, 12 Oct 2026 14:50:14 -0700 Subject: [PATCH 11/31] drm/xe: skip eviction of a BO with no resource xe_evict_flags() switches on tbo->resource->mem_type to choose an eviction placement. An xe BO can reach eviction with no ttm.resource, e.g. an imported dma-buf BO whose resource was freed during teardown while its attachment is still live: the exporter then invalidates it via xe_dma_buf_move_notify() -> xe_bo_evict(), and the switch dereferences a NULL resource and faults. A BO with no resource has nothing placed or mapped, so there is nothing to evict. Return an empty placement (num_placement = 0), mirroring the handling already used for non-xe scatter-gather BOs just above; ttm_bo_validate() then takes the pipeline-gutting path and returns 0. Without the fix the xe_dma_buf@xe_test_dmabuf_move_notify_no_resource dynamic live selftest will fail with below stack. BUG: kernel NULL pointer dereference, address: 0000000000000010 RIP: 0010:xe_evict_flags+0xf4/0x140 [xe] Call Trace: xe_bo_evict+0x40/0xa0 [xe] ? ttm_bo_pipeline_gutting+0x71/0x180 [ttm] xe_test_dmabuf_move_notify_no_resource+0x182/0x440 [xe] xe_dma_buf_kunit+0x387/0x580 [xe] v2: Add Fixes tag and failure info in commit text (Matt Brost) Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Niranjana Vishwanathapura Reviewed-by: Tejas Upadhyay Link: https://patch.msgid.link/20261012215014.1495677-5-niranjana.vishwanathapura@intel.com (cherry picked from commit dbcfaac6331f1fcb0c2fe3ad3e9adb1e5d43ec0c) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_bo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index d7849c7fb07e..1813c8ab0500 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -330,6 +330,12 @@ static void xe_evict_flags(struct ttm_buffer_object *tbo, return; } + /* No resource: nothing is placed/mapped, so there is nothing to evict. */ + if (!tbo->resource) { + placement->num_placement = 0; + return; + } + bo = ttm_to_xe_bo(tbo); if (bo->flags & XE_BO_FLAG_CPU_ADDR_MIRROR) { *placement = sys_placement; From fd8662823caa114a63f07222d2aa448f6ac5e864 Mon Sep 17 00:00:00 2001 From: Bommu Krishnaiah Date: Tue, 6 Oct 2026 07:33:14 +0530 Subject: [PATCH 12/31] drm/xe/oa: Zero OA buffer at allocation ttm_bo_type_kernel BOs are not guaranteed to be zeroed on allocation and may contain data from previously freed pages. Since the OA buffer is mmap'able by userspace, this can expose stale kernel memory contents. Streams opened with DRM_XE_OA_PROPERTY_OA_DISABLED allocate the OA buffer without calling xe_oa_init_oa_buffer(). Zero the buffer at allocation to prevent information disclosure through mmap. The existing zeroing in xe_oa_init_oa_buffer() remains necessary for zero report-id/timestamp detection on enable/re-enable. Fixes: 392bf22238ff ("drm/xe/oa/uapi: OA buffer mmap") Assisted-by: LLM Cc: Umesh Nerlige Ramappa Cc: Ghimiray, Himal Prasad Signed-off-by: Bommu Krishnaiah Reviewed-by: Himal Prasad Ghimiray Reviewed-by: Umesh Nerlige Ramappa Reviewed-by: Tejas Upadhyay Link: https://patch.msgid.link/20261006020314.2711762-1-krishnaiah.bommu@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit 126c4c5388f9311a390b9b254a835b0cf0f55331) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_oa.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index ab09dcff5860..64f01d9988a7 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -914,6 +914,13 @@ static int xe_oa_alloc_oa_buffer(struct xe_oa_stream *stream, size_t size) if (IS_ERR(bo)) return PTR_ERR(bo); + /* + * ttm_bo_type_kernel BOs are not zeroed on allocation, so the buffer can + * hold stale data from previously freed pages. Since the OA buffer is + * mmap'able by userspace, clear it here to avoid leaking kernel memory. + */ + xe_map_memset(stream->oa->xe, &bo->vmap, 0, 0, xe_bo_size(bo)); + stream->oa_buffer.bo = bo; stream->oa_buffer.bounce = kmalloc(stream->oa_buffer.format->size, GFP_KERNEL); From d2ce8fcd778ee6d92748c92d23a56d4416c29d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Mon, 12 Oct 2026 15:26:07 +0200 Subject: [PATCH 13/31] drm/xe: Bump LRU for external objects on exec and rebind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VM's private BOs get their LRU position refreshed in bulk via ttm_lru_bulk_move_tail() on vm->lru_bulk_move, both after a successful non-LR exec and after the LR preempt-fence rebind worker runs. External objects (BOs potentially shared across VMs) are never added to that per-VM bulk move list, so their LRU position is never refreshed by either path. This makes external objects in active use look more evictable than they are. Add xe_vm_bump_lru(), which, under a single lru_lock critical section, performs the bulk move of the VM's private BOs and bumps each external object locked by the drm_exec transaction. Use it from both the non-LR exec path and the LR rebind worker, replacing the open-coded bulk move. Fixes: d490ecf57790 ("drm/xe: Rework xe_exec and the VM rebind worker to use the drm_exec helper") Cc: Matthew Auld Cc: Matthew Brost Cc: # v6.8+ Assisted-by: LLM Signed-off-by: Thomas Hellström Reviewed-by: Matthew Brost Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20261012132607.21387-1-thomas.hellstrom@linux.intel.com (cherry picked from commit 324ba30cca180c2e1d02090743dd78ccb15b9e4a) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_exec.c | 7 ++----- drivers/gpu/drm/xe/xe_vm.c | 31 ++++++++++++++++++++++++++++--- drivers/gpu/drm/xe/xe_vm.h | 2 ++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c index d5293bc33a67..7dc9a0a1f569 100644 --- a/drivers/gpu/drm/xe/xe_exec.c +++ b/drivers/gpu/drm/xe/xe_exec.c @@ -346,11 +346,8 @@ retry: xe_sched_job_push(job); xe_vm_reactivate_rebind(vm); - if (!err && !xe_vm_in_lr_mode(vm)) { - spin_lock(&xe->ttm.lru_lock); - ttm_lru_bulk_move_tail(&vm->lru_bulk_move); - spin_unlock(&xe->ttm.lru_lock); - } + if (!err && !xe_vm_in_lr_mode(vm)) + xe_vm_bump_lru(vm, exec); if (mode == EXEC_MODE_LR) xe_hw_engine_group_resume_faulting_lr_jobs(group); diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index ef20e205a734..99e42bd29e79 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -410,6 +410,33 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, return 0; } +/** + * xe_vm_bump_lru() - Bump the LRU position of BOs used by a submission + * @vm: The vm for which we are bumping LRUs. + * @exec: The struct drm_exec with the locked GEM objects. + * + * Move the VM's private BOs, and all external objects locked by @exec, + * to the LRU tail. Private BOs are moved in bulk using @vm's + * &xe_vm.lru_bulk_move. External objects, which may be shared with other + * VMs, are not part of that bulk move and are bumped individually. + */ +void xe_vm_bump_lru(struct xe_vm *vm, struct drm_exec *exec) +{ + spinlock_t *lru_lock = &vm->xe->ttm.lru_lock; + struct drm_gem_object *obj; + + spin_lock(lru_lock); + ttm_lru_bulk_move_tail(&vm->lru_bulk_move); + drm_exec_for_each_locked_object(exec, obj) { + if (!drm_gpuvm_is_extobj(&vm->gpuvm, obj)) + continue; + + ttm_bo_move_to_lru_tail(&gem_to_xe_bo(obj)->ttm); + cond_resched_lock(lru_lock); + } + spin_unlock(lru_lock); +} + static int xe_preempt_work_begin(struct drm_exec *exec, struct xe_vm *vm, bool *done) { @@ -560,9 +587,7 @@ retry: #undef retry_required - spin_lock(&vm->xe->ttm.lru_lock); - ttm_lru_bulk_move_tail(&vm->lru_bulk_move); - spin_unlock(&vm->xe->ttm.lru_lock); + xe_vm_bump_lru(vm, &exec); /* Point of no return. */ arm_preempt_fences(vm, &preempt_fences); diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index c5b900f38ded..4b606445799c 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -284,6 +284,8 @@ int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma); int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, unsigned int num_fences); +void xe_vm_bump_lru(struct xe_vm *vm, struct drm_exec *exec); + struct dma_fence *xe_vm_bind_kernel_bo(struct xe_vm *vm, struct xe_bo *bo, struct xe_exec_queue *q, u64 addr, enum xe_cache_level cache_lvl); From 8907eb42c08b8eebe0f81fee256b34b5675d0e5a Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sat, 26 Sep 2026 16:20:27 -0400 Subject: [PATCH 14/31] drm/virtio: Reset device before deleting virtqueues on freeze virtgpu_freeze() waits for the control and cursor queues to drain and then deletes the virtqueues, but nothing tells the device to stop first. del_vqs() frees the vring backing, but does not provide a generic device quiesce operation. In particular, modern virtio-pci keeps enabled queues active until the device is reset, so the device is left DRIVER_OK with its queues pointing at memory that has been freed and may be reused while the guest is suspended. virtio_device_restore() only resets the device later, on resume. Reset the device before deleting the virtqueues, the way virtio_gpu_deinit() already does on removal. Fixes: cad6a879a7fb ("drm/virtio: Freeze and restore hooks to support suspend and resume") Cc: stable@vger.kernel.org Signed-off-by: Yuho Choi Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260926202040.1313385-1-oss.patchbox@gmail.com --- drivers/gpu/drm/virtio/virtgpu_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 2aaa7cb08085..52c174f9bff7 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -225,6 +225,7 @@ static int virtgpu_freeze(struct virtio_device *vdev) goto err_resume; } + virtio_reset_device(vdev); vdev->config->del_vqs(vdev); return 0; From 26401a4999f1596a515b2e704e7066ac09306488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 7 Oct 2026 14:46:57 +0200 Subject: [PATCH 15/31] drm/exec: don't immediately add prelocked obj to array of locked objs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a contention is detected the drm_exec object grabs a reference to the contended object, unlocks all other objects and pre-locks the contended one before anything else. The problem is that it is possible that this pre-locked object was about to be destroyed and drm_exec is now holding the last reference to it. So we never actually see this pre-locked object again to be explicitly locked and so also never reserve a slot for a dma_fence object on it. Should the driver now use drm_exec_for_each_locked_object() to add a new fence to all locked objects we eventually see a warning or even a BUG_ON() to prevent random memory corruption. Fix this by keeping the prelocked object out of the array of locked objs until it was explicitly mentioned by the driver again. If the prelocked object is never mentioned again by the driver just unlock and drop the reference at the end of the locking loop. Signed-off-by: Christian König Reported-by: Vitaly Prosyak Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Fixes: 09593216bff1 ("drm: execution context for GEM buffers v7") CC: stable@vger.kernel.org # v6.6+ Link: https://lore.kernel.org/r/20261007124657.9664-1-christian.koenig@amd.com --- drivers/gpu/drm/drm_exec.c | 77 ++++++++++++++++++++++---------------- include/drm/drm_exec.h | 15 +++++--- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 2453ec41360f..6fa3568ceba6 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -48,6 +48,16 @@ * See struct dma_exec for more details. */ +/* Unlock and put the prelocked obj */ +static void drm_exec_drop_prelocked(struct drm_exec *exec) +{ + if (exec->prelocked) { + dma_resv_unlock(exec->prelocked->resv); + drm_gem_object_put(exec->prelocked); + exec->prelocked = NULL; + } +} + /* Unlock all objects and drop references */ static void drm_exec_unlock_all(struct drm_exec *exec) { @@ -58,8 +68,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec) drm_gem_object_put(obj); } - drm_gem_object_put(exec->prelocked); - exec->prelocked = NULL; + drm_exec_drop_prelocked(exec); } /** @@ -118,6 +127,14 @@ EXPORT_SYMBOL(drm_exec_fini); bool drm_exec_cleanup(struct drm_exec *exec) { if (likely(!exec->contended)) { + + /* + * The contended and prelocked object might never be passed to + * drm_exec_lock_obj() again. This can happen when the object + * is about to be cleaned up and we now hold the last reference + * to it. Make sure that we don't unnecessarily keep it around. + */ + drm_exec_drop_prelocked(exec); ww_acquire_done(&exec->ticket); return false; } @@ -175,16 +192,16 @@ static int drm_exec_lock_contended(struct drm_exec *exec) dma_resv_lock_slow(obj->resv, &exec->ticket); } - ret = drm_exec_obj_locked(exec, obj); - if (unlikely(ret)) - goto error_unlock; + /* + * It is perfectly possible that we see another contention before the + * first one is fully handled. + */ + drm_exec_drop_prelocked(exec); + /* We move the reference from contended to prelocked here */ exec->prelocked = obj; return 0; -error_unlock: - dma_resv_unlock(obj->resv); - error_dropref: drm_gem_object_put(obj); return ret; @@ -212,35 +229,31 @@ int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj) if (exec->prelocked == obj) { drm_gem_object_put(exec->prelocked); exec->prelocked = NULL; - return 0; + } else { + if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) + ret = dma_resv_lock_interruptible(obj->resv, + &exec->ticket); + else + ret = dma_resv_lock(obj->resv, &exec->ticket); + + if (unlikely(ret == -EDEADLK)) { + drm_gem_object_get(obj); + exec->contended = obj; + return -EDEADLK; + } + + if (unlikely(ret == -EALREADY) && + exec->flags & DRM_EXEC_IGNORE_DUPLICATES) + return 0; + + if (unlikely(ret)) + return ret; } - if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) - ret = dma_resv_lock_interruptible(obj->resv, &exec->ticket); - else - ret = dma_resv_lock(obj->resv, &exec->ticket); - - if (unlikely(ret == -EDEADLK)) { - drm_gem_object_get(obj); - exec->contended = obj; - return -EDEADLK; - } - - if (unlikely(ret == -EALREADY) && - exec->flags & DRM_EXEC_IGNORE_DUPLICATES) - return 0; - - if (unlikely(ret)) - return ret; - ret = drm_exec_obj_locked(exec, obj); if (ret) - goto error_unlock; + dma_resv_unlock(obj->resv); - return 0; - -error_unlock: - dma_resv_unlock(obj->resv); return ret; } EXPORT_SYMBOL(drm_exec_lock_obj); diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index cc2937185a9f..c92d69a2c793 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -73,13 +73,15 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) /* Helper for drm_exec_for_each_locked_object(). Internal use only. */ #define __drm_exec_for_each_locked_object(exec, obj, __index) \ - for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); ++__index) + for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); \ + ++__index) /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object * @obj: the current GEM object * - * Iterate over all the locked GEM objects inside the drm_exec object. + * Iterate over all the explicitly locked GEM objects inside the drm_exec + * container, except for the contended and implicit prelocked one. */ #define drm_exec_for_each_locked_object(exec, obj) \ __drm_exec_for_each_locked_object(exec, obj, __UNIQUE_ID(drm_exec)) @@ -94,12 +96,13 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) * @exec: drm_exec object * @obj: the current GEM object * - * Iterate over all the locked GEM objects inside the drm_exec object in - * reverse locking order. Note that the internal index may wrap around, - * but that will be caught by drm_exec_obj(), returning a NULL object. + * Iterate over all the explicitly locked GEM objects inside the drm_exec + * container in reverse locking order. Note that the internal index may wrap + * around, but that will be caught by drm_exec_obj(), returning a NULL object. */ #define drm_exec_for_each_locked_object_reverse(exec, obj) \ - __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) + __drm_exec_for_each_locked_object_reverse(exec, obj, \ + __UNIQUE_ID(drm_exec)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked From 8bb1858db453cd4250e3b633f5d2d09206024ca6 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 22 Sep 2026 19:05:37 +0530 Subject: [PATCH 16/31] drm/i915/dmc: Harden DMC firmware payload parsing Validate the conversion of the DMC firmware size from dwords to bytes using check_mul_overflow() before calculating the firmware payload size. Reject zero-length payloads and zero MMIO counts in the DMC firmware header so malformed firmware is caught early. Also set dmc_fw_size only after the payload allocation succeeds. Fixes: eb805623d8b1 ("drm/i915/skl: Add support to load SKL CSR firmware.") Cc: stable@vger.kernel.org Assisted-by: Claude-Code:Sonnet-5 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Mitul Golani Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260922133535.3180973-5-dibin.moolakadan.subrahmanian@intel.com (cherry picked from commit efde6601a1f1daedfe3dcc2816c934ddb44e3eef) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_dmc.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index a191eee240d9..725481012578 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -1120,7 +1120,7 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc, } /* Cache the dmc header info. */ - if (mmio_count > mmio_count_max) { + if (!mmio_count || mmio_count > mmio_count_max) { drm_err(display->drm, "DMC firmware has wrong mmio count %u\n", mmio_count); return 0; } @@ -1166,13 +1166,21 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc, disable_dmc_evt(display, dmc_id, dmc_info->mmioaddr[i], dmc_info->mmiodata[i]) ? " (disabling)" : ""); } - dmc_info->mmio_count = mmio_count; - dmc_info->start_mmioaddr = start_mmioaddr; rem_size -= header_len_bytes; /* fw_size is in dwords, so multiplied by 4 to convert into bytes. */ - payload_size = dmc_header->fw_size * 4; + if (check_mul_overflow(dmc_header->fw_size, 4u, &payload_size)) { + drm_err(display->drm, "DMC fw_size too large (%u dwords)\n", + dmc_header->fw_size); + return 0; + } + + if (!payload_size) { + drm_err(display->drm, "DMC firmware has empty payload\n"); + return 0; + } + if (rem_size < payload_size) goto error_truncated; @@ -1180,12 +1188,15 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc, drm_err(display->drm, "DMC FW too big (%u bytes)\n", payload_size); return 0; } - dmc_info->dmc_fw_size = dmc_header->fw_size; dmc_info->payload = kmalloc(payload_size, GFP_KERNEL); if (!dmc_info->payload) return 0; + dmc_info->mmio_count = mmio_count; + dmc_info->start_mmioaddr = start_mmioaddr; + dmc_info->dmc_fw_size = dmc_header->fw_size; + payload = (u8 *)(dmc_header) + header_len_bytes; memcpy(dmc_info->payload, payload, payload_size); From 7dedc5bb11f95e96fe80757c924d8d9aa7327759 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 22 Sep 2026 19:05:38 +0530 Subject: [PATCH 17/31] drm/i915/dmc: Add sanity check for DMC load address For DMC firmware header version 3, the firmware load address is stored in dmc_info->start_mmioaddr and later used by dmc_load_program(). Unlike the MMIO address table, the firmware load address is not validated. Add a sanity check to ensure it is within the valid range. BSpec: 69671, 68378 Fixes: 3d5928a168a9 ("drm/i915/xelpd: Pipe A DMC plugging") Cc: stable@vger.kernel.org Assisted-by: Claude-Code:Sonnet-5 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Mitul Golani Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260922133535.3180973-6-dibin.moolakadan.subrahmanian@intel.com (cherry picked from commit 5e47ee5c76ca9becde8df4ad5202845b7ffcbc4c) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_dmc.c | 55 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_dmc_regs.h | 12 ++++ 2 files changed, 67 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 725481012578..6611765fd62c 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -1023,6 +1023,55 @@ static void dmc_set_fw_offset(struct intel_dmc *dmc, } } +/* + * Check if the load address is within the valid range for the given DMC ID. + */ +static bool dmc_load_addr_sanity_check(struct intel_dmc *dmc, + u32 start_addr, u32 payload_size, + int header_ver, enum intel_dmc_id dmc_id) +{ + u32 start_range, end_range, end_addr; + + if (header_ver != 3) + return true; + + switch (dmc_id) { + case DMC_FW_MAIN: + start_range = DMC_MAIN_PROGRAM_BASE_START; + end_range = DMC_MAIN_PROGRAM_BASE_END; + break; + case DMC_FW_PIPEA: + start_range = DMC_PIPEA_PROGRAM_BASE_START; + end_range = DMC_PIPEA_PROGRAM_BASE_END; + break; + case DMC_FW_PIPEB: + start_range = DMC_PIPEB_PROGRAM_BASE_START; + end_range = DMC_PIPEB_PROGRAM_BASE_END; + break; + case DMC_FW_PIPEC: + start_range = DMC_PIPEC_PROGRAM_BASE_START; + end_range = DMC_PIPEC_PROGRAM_BASE_END; + break; + case DMC_FW_PIPED: + start_range = DMC_PIPED_PROGRAM_BASE_START; + end_range = DMC_PIPED_PROGRAM_BASE_END; + break; + default: + MISSING_CASE(dmc_id); + return false; + } + + if (payload_size == 0) + end_addr = start_addr; + else if (check_add_overflow(start_addr, payload_size - 1, &end_addr)) + return false; + + if (start_addr < start_range || end_addr > end_range) + return false; + + return true; +} + static bool dmc_mmio_addr_sanity_check(struct intel_dmc *dmc, const u32 *mmioaddr, u32 mmio_count, int header_ver, enum intel_dmc_id dmc_id) @@ -1189,6 +1238,12 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc, return 0; } + if (!dmc_load_addr_sanity_check(dmc, start_mmioaddr, payload_size, + dmc_header->header_ver, dmc_id)) { + drm_err(display->drm, "DMC %d: firmware has wrong load address\n", dmc_id); + return 0; + } + dmc_info->payload = kmalloc(payload_size, GFP_KERNEL); if (!dmc_info->payload) return 0; diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h index 6b7978fb8986..324320afad58 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h +++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h @@ -521,6 +521,18 @@ enum pipedmc_event_id { #define TGL_PIPE_MMIO_END(dmc_id) _PICK_EVEN(((dmc_id) - 1), _TGL_PIPEA_MMIO_END,\ _TGL_PIPEB_MMIO_END) +/* For DMC header version v3*/ +#define DMC_MAIN_PROGRAM_BASE_START 0x80000 +#define DMC_MAIN_PROGRAM_BASE_END 0x86fff +#define DMC_PIPEA_PROGRAM_BASE_START 0x90000 +#define DMC_PIPEA_PROGRAM_BASE_END 0x96fff +#define DMC_PIPEB_PROGRAM_BASE_START 0x98000 +#define DMC_PIPEB_PROGRAM_BASE_END 0x9efff +#define DMC_PIPEC_PROGRAM_BASE_START 0x52000 +#define DMC_PIPEC_PROGRAM_BASE_END 0x53fff +#define DMC_PIPED_PROGRAM_BASE_START 0x59000 +#define DMC_PIPED_PROGRAM_BASE_END 0x5afff + #define SKL_DMC_DC3_DC5_COUNT _MMIO(0x80030) #define SKL_DMC_DC5_DC6_COUNT _MMIO(0x8002C) #define BXT_DMC_DC3_DC5_COUNT _MMIO(0x80038) From 6a8bf59de3ef47ff51face6ea826395a1098baa0 Mon Sep 17 00:00:00 2001 From: Dnyaneshwar Bhadane Date: Mon, 21 Sep 2026 14:32:26 +0530 Subject: [PATCH 18/31] drm/i915/cx0_phy: Fix ARL-S host bridge detection Fix is_arrowlake_s_by_host_bridge() which always returned false. The while loop always exits with pdev as NULL, so the return condition was always false, causing the wrong tx_misc value to be used. Check the host bridge ID inside the loop body. Once found early exit from the loop release the pci reference "pdev" and return true. v2: - Update commit message (Chaitanya) Bspec:74165,74491 Suggested-by: Jani Nikula Cc: Fixes: 3630a47b700e ("drm/i915/cx0_phy: Update HDMI TMDS C20 algorithm value") Signed-off-by: Dnyaneshwar Bhadane Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/20260921090226.946472-1-dnyaneshwar.bhadane@intel.com (cherry picked from commit 0507cc5a476af0b6d037b19248bdd20dbe27487d) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index dbebd7210848..de450ebf541f 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2342,12 +2342,14 @@ static void intel_c10pll_dump_hw_state(struct drm_printer *p, static bool is_arrowlake_s_by_host_bridge(void) { struct pci_dev *pdev = NULL; - u16 host_bridge_pci_dev_id; + while ((pdev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, pdev))) { + if (IS_ARROWLAKE_S_BY_HOST_BRIDGE_ID(pdev->device)) { + pci_dev_put(pdev); + return true; + } + } - while ((pdev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, pdev))) - host_bridge_pci_dev_id = pdev->device; - - return pdev && IS_ARROWLAKE_S_BY_HOST_BRIDGE_ID(host_bridge_pci_dev_id); + return false; } static u16 intel_c20_hdmi_tmds_tx_cgf_1(struct intel_display *display) From c347fef5f8f8927985bd67835331ce4cf7ff43a4 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Wed, 14 Oct 2026 09:09:01 +0200 Subject: [PATCH 19/31] drm/panthor: fix immortalis condition Exactly 10 cores is also valid for an Immortalis configuration, according to the public documentation: https://support.arm.com/compute-ip/immortalis-g715 So let's report this G715 with 10 cores as Immortalis as well, as long as it supports the ray-intersection feature. This matches what we do for more recent GPUs as well. Fixes: 3b1dc21d6d80 ("drm/panthor: Add support for Mali-Gx15 family of GPUs") Signed-off-by: Erik Faye-Lund Reviewed-by: Liviu Dudau Reviewed-by: Boris Brezillon Reviewed-by: Steven Price Link: https://patch.msgid.link/20261014070902.169273-1-erik.faye-lund@arm.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c index 7e315708ca7c..ea1e222ef121 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.c +++ b/drivers/gpu/drm/panthor/panthor_hw.c @@ -136,7 +136,7 @@ static char *get_gpu_model_name(struct panthor_device *ptdev) case GPU_PROD_ID_MAKE(10, 7): return "Mali-G610"; case GPU_PROD_ID_MAKE(11, 2): - if (shader_core_count > 10 && ray_intersection) + if (shader_core_count >= 10 && ray_intersection) return "Mali-G715-Immortalis"; else if (shader_core_count >= 7) return "Mali-G715"; From 800ea86e83e137395eba54103e5c459c2bc9965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 6 Oct 2026 23:16:07 +0200 Subject: [PATCH 20/31] drm/amdgpu/uvd3.1: Fix checking whether UVD firmware keyselect was already set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PASS_MASK is for a different register not for UVD_FW_START. It was a mistake to check this flag. It should be enough to check that the register is non-zero. Fixes: 889a2cfd889c ("drm/amdgpu/uvd3.1: Don't validate the firmware when already validated") Signed-off-by: Timur Kristóf Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20261006211607.75407-1-timur.kristof@gmail.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 9dee462c83259ca769aa1bf82228adee9831685c) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c index efb3fde919ee..b7d451f439a7 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c @@ -288,7 +288,7 @@ static int uvd_v3_1_fw_validate(struct amdgpu_device *adev) int i; uint32_t keysel = adev->uvd.keyselect; - if (RREG32(mmUVD_FW_START) & UVD_FW_STATUS__PASS_MASK) { + if (RREG32(mmUVD_FW_START)) { dev_dbg(adev->dev, "UVD keyselect already set: 0x%x (on CPU: 0x%x)\n", RREG32(mmUVD_FW_START), adev->uvd.keyselect); return 0; From b5629f4a0869f4b01cfbfdf27f2c349d68c5d9d4 Mon Sep 17 00:00:00 2001 From: Perry Yuan Date: Thu, 10 Sep 2026 16:57:31 +0800 Subject: [PATCH 21/31] drm/amdgpu: fix sched entity leak in ttm buffer entity init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_ttm_buffer_entity_init() checks num_gart_windows after drm_sched_entity_init() has already attached the entity to a scheduler run-queue. Returning -EINVAL from that check leaks the entity. Move the ARRAY_SIZE() check before any resources are acquired. Fixes: 2c3725572561 ("drm/amdgpu: statically assign gart windows to ttm entities") Signed-off-by: Perry Yuan Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 4000d1cdd1eacd3422267d6c8b4f1c9333b0317e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 3838e464703c..8569dc8150b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2065,14 +2065,15 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, int i, r, num_pages; const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); + if (ARRAY_SIZE(entity->gart_window_offs) < num_gart_windows) + return -EINVAL; + r = drm_sched_entity_init(&entity->base, prio, scheds, num_schedulers, NULL); if (r) return r; mutex_init(&entity->lock); - if (ARRAY_SIZE(entity->gart_window_offs) < num_gart_windows) - return -EINVAL; if (num_gart_windows == 0) return 0; From 5b0a459f990be2faba33229fd93f10f23745fab2 Mon Sep 17 00:00:00 2001 From: Perry Yuan Date: Thu, 10 Sep 2026 16:58:17 +0800 Subject: [PATCH 22/31] drm/amdgpu: destroy mutex when tearing down ttm buffer entity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pair mutex_init() with mutex_destroy() on the GART window allocation failure path and in amdgpu_ttm_buffer_entity_fini(). This is a no-op without CONFIG_DEBUG_MUTEXES, but it keeps lockdep from seeing a live mutex after the entity is freed or re-initialized. Signed-off-by: Perry Yuan Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 03ac40aeed034a0a0d5824ad26f8fbe1f6463698) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 8569dc8150b7..83968886c29a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2081,6 +2081,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, r = amdgpu_gtt_mgr_alloc_entries(mgr, &entity->gart_node, num_pages, DRM_MM_INSERT_BEST); if (r) { + mutex_destroy(&entity->lock); drm_sched_entity_destroy(&entity->base); return r; } @@ -2098,6 +2099,7 @@ static void amdgpu_ttm_buffer_entity_fini(struct amdgpu_gtt_mgr *mgr, struct amdgpu_ttm_buffer_entity *entity) { amdgpu_gtt_mgr_free_entries(mgr, &entity->gart_node); + mutex_destroy(&entity->lock); drm_sched_entity_destroy(&entity->base); } From a2eae9f4fa1b07f8e6e61b48931228da051943f0 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 8 Oct 2026 20:28:09 +0800 Subject: [PATCH 23/31] drm/amd/pm: add zero fan stop temperature support on smu 14.0.2 add zero fan stop temperature support on smu 14.0.2 Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5905 Signed-off-by: Kenneth Feng Reviewed-by: Kevin Wang Signed-off-by: Alex Deucher (cherry picked from commit f990c94a2633d0ead477eb1b174e254a6d292e66) Cc: stable@vger.kernel.org --- .../drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index ed99e61f18e1..6621e50de965 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -89,6 +89,7 @@ static const struct smu_feature_bits smu_v14_0_2_dpm_features = { #define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9 #define PP_OD_FEATURE_FAN_MINIMUM_PWM 10 #define PP_OD_FEATURE_FAN_ZERO_RPM_ENABLE 11 +#define PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP 12 static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1), @@ -1020,6 +1021,10 @@ static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu, od_min_setting = overdrive_lowerlimits->FanZeroRpmEnable; od_max_setting = overdrive_upperlimits->FanZeroRpmEnable; break; + case PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP: + od_min_setting = pptable->SkuTable.MsgLimits.FanStopTempMin; + od_max_setting = pptable->SkuTable.MsgLimits.FanStopTempMax; + break; default: od_min_setting = od_max_setting = INT_MAX; break; @@ -1244,6 +1249,24 @@ static int smu_v14_0_2_emit_clk_levels(struct smu_context *smu, min_value, max_value); break; + case SMU_OD_FAN_ZERO_RPM_STOP_TEMP: + if (!smu_v14_0_2_is_od_feature_supported(smu, + PP_OD_FEATURE_ZERO_FAN_BIT)) + break; + + size += sysfs_emit_at(buf, size, "FAN_ZERO_RPM_STOP_TEMPERATURE:\n"); + size += sysfs_emit_at(buf, size, "%d\n", + (int)od_table->OverDriveTable.FanZeroRpmStopTemp); + + size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE"); + smu_v14_0_2_get_od_setting_limits(smu, + PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP, + &min_value, + &max_value); + size += sysfs_emit_at(buf, size, "ZERO_RPM_STOP_TEMPERATURE: %u %u\n", + min_value, max_value); + break; + case SMU_OD_RANGE: if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) && !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) && @@ -2311,7 +2334,9 @@ static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu) OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE | OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET | OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_RETRIEVE | - OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_SET; + OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_SET | + OD_OPS_SUPPORT_FAN_ZERO_RPM_STOP_TEMP_RETRIEVE | + OD_OPS_SUPPORT_FAN_ZERO_RPM_STOP_TEMP_SET; } static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu, @@ -2392,6 +2417,8 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu) user_od_table_bak.OverDriveTable.FanMinimumPwm; user_od_table->OverDriveTable.FanZeroRpmEnable = user_od_table_bak.OverDriveTable.FanZeroRpmEnable; + user_od_table->OverDriveTable.FanZeroRpmStopTemp = + user_od_table_bak.OverDriveTable.FanZeroRpmStopTemp; } smu_v14_0_2_set_supported_od_feature_mask(smu); @@ -2446,6 +2473,11 @@ static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long inp boot_overdrive_table->OverDriveTable.FanZeroRpmEnable; od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT); break; + case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP: + od_table->OverDriveTable.FanZeroRpmStopTemp = + boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp; + od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT); + break; case PP_OD_EDIT_ACOUSTIC_LIMIT: od_table->OverDriveTable.AcousticLimitRpmThreshold = boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold; @@ -2752,6 +2784,27 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu, od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT); break; + case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP: + if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_ZERO_FAN_BIT)) { + dev_warn(adev->dev, "Zero RPM setting not supported!\n"); + return -ENOTSUPP; + } + + smu_v14_0_2_get_od_setting_limits(smu, + PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP, + &minimum, + &maximum); + if (input[0] < minimum || + input[0] > maximum) { + dev_info(adev->dev, "zero RPM stop temperature setting(%ld) must be within [%d, %d]!\n", + input[0], minimum, maximum); + return -EINVAL; + } + + od_table->OverDriveTable.FanZeroRpmStopTemp = input[0]; + od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT); + break; + case PP_OD_RESTORE_DEFAULT_TABLE: if (size == 1) { ret = smu_v14_0_2_od_restore_table_single(smu, input[0]); From 3c516a5646285442e1cd71dff2584b77eb4fc421 Mon Sep 17 00:00:00 2001 From: Perry Yuan Date: Thu, 10 Sep 2026 17:13:39 +0800 Subject: [PATCH 24/31] drm/amdgpu: fix GEM object leak on ENOMEM in userptr ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When amdgpu_hmm_range_alloc() fails amdgpu_gem_userptr_ioctl() returns -ENOMEM right away and skips the cleanup every other error path goes through. The GEM object, the bo->parent reference on the VM root and the MMU notifier registration are all left behind. Use release_object here, not user_pages_done, range is still NULL at this point. Fixes: e095b55155ef ("drm/amdgpu: use user provided hmm_range buffer in amdgpu_ttm_tt_get_user_pages") Signed-off-by: Perry Yuan Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit bbe739e407a542073ec1a7872b14843566b4919e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 165344f7197f..3a51efac9481 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -567,8 +567,10 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, if (args->flags & AMDGPU_GEM_USERPTR_VALIDATE) { range = amdgpu_hmm_range_alloc(NULL); - if (unlikely(!range)) - return -ENOMEM; + if (unlikely(!range)) { + r = -ENOMEM; + goto release_object; + } r = amdgpu_ttm_tt_get_user_pages(bo, range); if (r) { amdgpu_hmm_range_free(range); From de9f6bcdb44f0e410031002e2f103826ea6b6259 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Fri, 9 Oct 2026 13:21:33 +0800 Subject: [PATCH 25/31] drm/amdgpu: don't drop the runtime PM reference twice in amdgpu_gpr amdgpu_debugfs_gpr_read() drops its runtime PM reference right after reading the wave registers. When copying them to userspace fails, it jumps to the err label, which drops the reference a second time. A read into a bad buffer on Navi31 either steals a reference somebody else holds (power/runtime_usage goes from 1 to 0) or underflows the count: amdgpu 0000:03:00.0: Runtime PM usage count underflow! The registers are already in the kernel buffer at that point, so finish the hardware access first and copy the buffer with a single copy_to_user() instead of the put_user() loop with its own error path. Fixes: 9eee152aab56 ("drm/amdgpu/debugfs: fix ref count leak when pm_runtime_get_sync fails") Reviewed-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 31035bbd74f7c3e10c163db875b8e67f8b50ee6a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 23 ++++----------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index ab7ccfaea22a..4f9b72ac5698 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1266,7 +1266,6 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, { struct amdgpu_device *adev = f->f_inode->i_private; int r; - ssize_t result = 0; uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data; if (size > 4096 || size & 3 || *pos & 3) @@ -1310,25 +1309,11 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, mutex_unlock(&adev->grbm_idx_mutex); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); - - while (size) { - uint32_t value; - - value = data[result >> 2]; - r = put_user(value, (uint32_t *)buf); - if (r) { - amdgpu_virt_disable_access_debugfs(adev); - goto err; - } - - result += 4; - buf += 4; - size -= 4; - } - - kfree(data); amdgpu_virt_disable_access_debugfs(adev); - return result; + + r = copy_to_user(buf, data, size) ? -EFAULT : size; + kfree(data); + return r; err: pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); From 1b4da805a0ba354a549ce07459c871c17dd94fe0 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Fri, 9 Oct 2026 16:05:05 +0800 Subject: [PATCH 26/31] drm/amdgpu: free the userptr ranges when a CS fails after parser_bos amdgpu_cs_parser_bos() allocates an HMM range for every userptr entry of the BO list and looks up the user pages. Those ranges are only freed when amdgpu_cs_parser_bos() itself fails or when amdgpu_cs_submit() checks them under the notifier lock. If amdgpu_cs_patch_jobs(), amdgpu_cs_vm_handling(), amdgpu_cs_sync_rings() or the early part of amdgpu_cs_submit() fails, nothing frees them: neither amdgpu_cs_parser_fini() nor amdgpu_bo_list_free() touch e->range. Each such CS then leaks the range, its hmm_pfns array (8 bytes per page of the userptr BO) and the BO reference the range holds. An IB on a ring that patches the CS in place (e.g. VCN) with an unmapped va_start fails in amdgpu_cs_patch_ibs() and takes this path. Free the ranges on those error paths. Ranges amdgpu_cs_submit() already dropped are NULL, so freeing them again is a no-op. Fixes: fec8fdb54e8f ("drm/amdgpu: fix userptr HMM range handling v2") Reviewed-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit c52a9542ed7cfd527feb938bd0accada2ac8d267) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 03b41f803520..ef8a61ce4b1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1431,6 +1431,7 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_bo_list_entry *e; struct amdgpu_cs_parser parser; int r; @@ -1465,25 +1466,31 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = amdgpu_cs_patch_jobs(&parser); if (r) - goto error_fini; + goto error_free_user_pages; r = amdgpu_cs_vm_handling(&parser); if (r) - goto error_fini; + goto error_free_user_pages; r = amdgpu_cs_sync_rings(&parser); if (r) - goto error_fini; + goto error_free_user_pages; trace_amdgpu_cs_ibs(&parser); r = amdgpu_cs_submit(&parser, data); if (r) - goto error_fini; + goto error_free_user_pages; amdgpu_cs_parser_fini(&parser); return 0; +error_free_user_pages: + /* amdgpu_cs_submit() only drops the ranges once it got that far. */ + amdgpu_bo_list_for_each_userptr_entry(e, parser.bo_list) { + amdgpu_hmm_range_free(e->range); + e->range = NULL; + } error_fini: amdgpu_cs_parser_fini(&parser); return r; From d65a459dbc9a6c3b051e22c96f73598e34b31bfc Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Mon, 12 Oct 2026 09:28:33 +0800 Subject: [PATCH 27/31] drm/amdgpu: report the BO alignment in bytes in GEM_LIST_HANDLES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The uapi documents drm_amdgpu_gem_list_handles_entry.alignment as "physical start_addr alignment in bytes", and GEM_OP GET_GEM_CREATE_INFO returns the same value in bytes, but GEM_LIST_HANDLES returns tbo.page_alignment, which is in pages. For a BO created with a 64 KiB alignment on navi31: GET_GEM_CREATE_INFO: alignment=65536 GEM_LIST_HANDLES: alignment=16 so a user such as CRIU that recreates BOs from this list uses the wrong alignment. Convert it to bytes. Fixes: f9db1fc52ceb ("drm/amdgpu: Add ioctl to get all gem handles for a process") Signed-off-by: Jesse Zhang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit aa4493ae949c13e79aba0731394dd7a7e431342b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 3a51efac9481..ac7c8bb591eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1238,7 +1238,7 @@ int amdgpu_gem_list_handles_ioctl(struct drm_device *dev, void *data, bo_entry->alloc_flags = bo->flags & AMDGPU_GEM_CREATE_SETTABLE_MASK; bo_entry->preferred_domains = bo->preferred_domains; bo_entry->gem_handle = id; - bo_entry->alignment = bo->tbo.page_alignment; + bo_entry->alignment = (u64)bo->tbo.page_alignment << PAGE_SHIFT; if (bo->tbo.base.import_attach) bo_entry->flags |= AMDGPU_GEM_LIST_HANDLES_FLAG_IS_IMPORT; From 32d0771ef4ca69c30a1db3aefa34dd177cd2a047 Mon Sep 17 00:00:00 2001 From: Benjamin Cheng Date: Fri, 9 Oct 2026 09:31:42 -0400 Subject: [PATCH 28/31] drm/amdgpu/userq: actually validate userq BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was supposed to loop over the individual evicted list, as the kernel and always_valid evicted lists were already handled in amdgpu_vm_validate. Without this, BOs that were created and evicted before a userq was made would never end up in an eviction fence rearm. Also add the missing ref to the BO before drm_exec locks and refs it. Fixes: 4cdbba5a16aa ("drm/amdgpu: restructure VM state machine v4") Signed-off-by: Benjamin Cheng Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 18decfc9e8cc0fa70b6de9944cc27a37729edfbd) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 9316423f9340..b3dc5dd49a64 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -980,19 +980,21 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec, { struct ttm_operation_ctx ctx = { false, false }; struct amdgpu_bo_va *bo_va; - struct amdgpu_bo *bo; + struct amdgpu_bo *bo, *tmp; int ret; spin_lock(&vm->individual_lock); - while (!list_empty(&vm->always_valid.evicted)) { - bo_va = list_first_entry(&vm->always_valid.evicted, + while (!list_empty(&vm->individual.evicted)) { + bo_va = list_first_entry(&vm->individual.evicted, struct amdgpu_bo_va, base.vm_status); + bo = bo_va->base.bo; + tmp = amdgpu_bo_ref(bo); spin_unlock(&vm->individual_lock); - bo = bo_va->base.bo; ret = drm_exec_prepare_obj(exec, &bo->tbo.base, TTM_NUM_MOVE_FENCES + 1); + amdgpu_bo_unref(&tmp); if (unlikely(ret)) return ret; From 55a67aa89342545b89f469dea6f29bfb6fac6f3d Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 17 Sep 2026 11:44:33 +0530 Subject: [PATCH 29/31] drm/amd/display: Fix NULL sink dereference in USB4 bandwidth functions allocate_usb4_bandwidth_for_stream() and allocate_usb4_bandwidth() both use ASSERT(stream->sink) before dereferencing stream->sink->link. ASSERT is compiled out in production builds, so a NULL stream->sink causes a NULL pointer dereference. link_set_dpms_off() already guards two earlier accesses to stream->sink with explicit NULL checks, confirming that stream->sink can legitimately be NULL. The subsequent call to deallocate_usb4_bandwidth() had no equivalent guard. Replace the ASSERT() calls with proper NULL checks that return false early, and add a matching stream->sink guard at the call site in link_set_dpms_off() consistent with the surrounding pattern. v2: Keep the existing ASSERT() calls for diagnostics (to report unexpected NULL sink), and add explicit NULL checks that return false before dereferencing stream->sink. Also add a matching stream->sink guard at the deallocation call site in link_set_dpms_off(). (Alex) Fixes: 8fa813b7fd1e ("drm/amd/display: Remove sink usage from DPMS") Reported-by: Dan Carpenter Cc: Nicholas Kazlauskas Cc: Dominik Kaszewski Cc: George Zhang Cc: Aurabindo Pillai Cc: Roman Li Cc: Ivan Lipski Cc: Dan Wheeler Cc: Alex Hung Cc: Tom Chung Signed-off-by: Srinivasan Shanmugam Tested-by: Dan Wheeler Reviewed-by: Roman Li Signed-off-by: Alex Deucher (cherry picked from commit 98029a0ce61feb52c563f65349dd9b25f672e355) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/display/dc/link/link_dpms.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 48b086d15ab0..25ec3d38a14e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -2306,9 +2306,14 @@ static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, i { ASSERT(stream->sink); - struct dc_link *link = stream->sink->link; + struct dc_link *link; int req_bw = stream_bw; + if (!stream->sink) + return false; + + link = stream->sink->link; + DC_LOGGER_INIT(link->ctx->logger); if (!link->dpia_bw_alloc_config.bw_alloc_enabled) @@ -2355,9 +2360,13 @@ static bool allocate_usb4_bandwidth(struct dc_stream_state *stream) ASSERT(stream->sink); bool ret; + int bw; - int bw = dc_bandwidth_in_kbps_from_timing(&stream->timing, - dc_link_get_highest_encoding_format(stream->sink->link)); + if (!stream->sink) + return false; + + bw = dc_bandwidth_in_kbps_from_timing(&stream->timing, + dc_link_get_highest_encoding_format(stream->sink->link)); ret = allocate_usb4_bandwidth_for_stream(stream, bw); @@ -2419,7 +2428,8 @@ enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx) update_psp_stream_config(pipe_ctx, true); dc->hwss.blank_stream(pipe_ctx); - if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation) + if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation && + stream->sink) deallocate_usb4_bandwidth(stream); if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) From 3c44cbcec5eb370713bbd2c315ed7c34d8ccec2b Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Mon, 12 Oct 2026 12:24:03 +0800 Subject: [PATCH 30/31] drm/amdgpu: keep the eviction fence until the last handle is closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_gem_object_close() detaches the user queue eviction fence from the BO before it checks bo_va->ref_count, so closing any handle of a BO removes the fence, even when the file still has another handle to the BO and the bo_va with its GPU mappings stays. The BO can then be evicted without suspending the user queues of the file, which keep accessing it through mappings that are only marked for update. Seen on navi31 with a SDMA user queue: a BO opened twice in the same file (GEM_FLINK + GEM_OPEN) loses its eviction fence when the second handle is closed (traced attach/detach of the fence): attach bo=ffff8c78a6c84c00 GEM_CLOSE handle=7 detach bo=ffff8c78a6c84c00 while handle 6 and the mapping are still in use. Only detach the fence when the last reference to the bo_va goes away, matching amdgpu_gem_object_open(), which attaches it only when the bo_va is created. With this the fence stays until the file is closed. Fixes: 31f7efcdca4d ("drm/amdgpu: enable eviction fence") Signed-off-by: Jesse Zhang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8387982f03d4026b7dc11b0b5e3118298eee4d6a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index ac7c8bb591eb..afd8502a2bc4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -337,11 +337,14 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj, goto out_unlock; } + bo_va = amdgpu_vm_bo_find(vm, bo); + /* the fence is attached once per bo_va, so keep it for other handles */ + if (bo_va && --bo_va->ref_count) + goto out_unlock; + if (!amdgpu_vm_is_bo_always_valid(vm, bo)) amdgpu_evf_mgr_detach_fence(&fpriv->evf_mgr, bo); - - bo_va = amdgpu_vm_bo_find(vm, bo); - if (!bo_va || --bo_va->ref_count) + if (!bo_va) goto out_unlock; amdgpu_vm_bo_del(adev, bo_va); From 752480ad62964edf0254e151110d3e53a7cb37de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Wed, 7 Oct 2026 00:02:52 +0200 Subject: [PATCH 31/31] drm/amdgpu/sdma3: Work around POLL_REGMEM / FENCE hang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDMA can randomly hang on Polaris 10. Hard to reproduce, but has been occurring sporadically in the Mesa CI on the RADV Polaris 10 job even when Mesa isn't using the SDMA engine. When Mesa actually uses the SDMA on Polaris, the hang becomes more frequent and the reproduction rate goes up significantly in the CI system. We don't know why the hangs occur, but it seems that inserting a single NOP packet before each POLL_REGMEM and FENCE packet works around it. At the moment it's unclear which GPUs are affected, so let's apply this to SDMA 3.x for now. Note that this issue is not documented or acknowledged by AMD. Reviewed-by: Alex Deucher Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit c343a27ed90823d08de125f7c447d96d056b75d9) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 05cce4bde73e..c390767c9088 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -457,6 +457,9 @@ static void sdma_v3_0_ring_emit_hdp_flush(struct amdgpu_ring *ring) else ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA1, 1); + /* Hang workaround */ + amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP)); + amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) | SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) | SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */ @@ -484,6 +487,10 @@ static void sdma_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se unsigned flags) { bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT; + + /* Hang workaround */ + amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP)); + /* write the fence */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); amdgpu_ring_write(ring, lower_32_bits(addr)); @@ -492,7 +499,11 @@ static void sdma_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se /* optionally write high bits as well */ if (write64bit) { + /* Hang workaround */ + amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP)); + addr += 4; + amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); @@ -1033,6 +1044,9 @@ static void sdma_v3_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring) uint32_t seq = ring->fence_drv.sync_seq; uint64_t addr = ring->fence_drv.gpu_addr; + /* Hang workaround */ + amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP)); + /* wait for idle */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) | SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) | @@ -1061,6 +1075,9 @@ static void sdma_v3_0_ring_emit_vm_flush(struct amdgpu_ring *ring, { amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr); + /* Hang workaround */ + amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP)); + /* wait for flush */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) | SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) | @@ -1510,11 +1527,11 @@ static const struct amdgpu_ring_funcs sdma_v3_0_ring_funcs = { .get_wptr = sdma_v3_0_ring_get_wptr, .set_wptr = sdma_v3_0_ring_set_wptr, .emit_frame_size = - 6 + /* sdma_v3_0_ring_emit_hdp_flush */ + 6 + 1 + /* sdma_v3_0_ring_emit_hdp_flush */ 3 + /* hdp invalidate */ - 6 + /* sdma_v3_0_ring_emit_pipeline_sync */ - VI_FLUSH_GPU_TLB_NUM_WREG * 3 + 6 + /* sdma_v3_0_ring_emit_vm_flush */ - 10 + 10 + 10, /* sdma_v3_0_ring_emit_fence x3 for user fence, vm fence */ + 6 + 1 + /* sdma_v3_0_ring_emit_pipeline_sync */ + VI_FLUSH_GPU_TLB_NUM_WREG * 3 + 6 + 1 + /* sdma_v3_0_ring_emit_vm_flush */ + (10 + 1) * 3, /* sdma_v3_0_ring_emit_fence x3 for user fence, vm fence */ .emit_ib_size = 7 + 6, /* sdma_v3_0_ring_emit_ib */ .emit_ib = sdma_v3_0_ring_emit_ib, .emit_fence = sdma_v3_0_ring_emit_fence,