From 3fcf923302a8f5c0dc3af3d2ca2657cb5fae4297 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 14 Apr 2026 13:10:08 +0200 Subject: hv: utils: handle and propagate errors in kvp_register Make kvp_register() return an error code instead of silently ignoring failures, and propagate the error from kvp_handle_handshake() instead of returning success. This propagates both kzalloc_obj() and hvutil_transport_send() failures to kvp_handle_handshake() and thus to kvp_on_msg(). Fixes: 245ba56a52a3 ("Staging: hv: Implement key/value pair (KVP)") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Long Li Signed-off-by: Wei Liu --- drivers/hv/hv_kvp.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0d73daf745a7..6180ebe040ff 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -93,7 +93,7 @@ static void kvp_send_key(struct work_struct *dummy); static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); static void kvp_timeout_func(struct work_struct *dummy); static void kvp_host_handshake_func(struct work_struct *dummy); -static void kvp_register(int); +static int kvp_register(int); static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func); static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func); @@ -127,24 +127,26 @@ static void kvp_register_done(void) hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); } -static void +static int kvp_register(int reg_value) { struct hv_kvp_msg *kvp_msg; char *version; + int ret; kvp_msg = kzalloc_obj(*kvp_msg); + if (!kvp_msg) + return -ENOMEM; - if (kvp_msg) { - version = kvp_msg->body.kvp_register.version; - kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); + version = kvp_msg->body.kvp_register.version; + kvp_msg->kvp_hdr.operation = reg_value; + strcpy(version, HV_DRV_VERSION); - hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), - kvp_register_done); - kfree(kvp_msg); - } + ret = hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), + kvp_register_done); + kfree(kvp_msg); + return ret; } static void kvp_timeout_func(struct work_struct *dummy) @@ -186,9 +188,8 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) */ pr_debug("KVP: userspace daemon ver. %d connected\n", msg->kvp_hdr.operation); - kvp_register(dm_reg_value); - return 0; + return kvp_register(dm_reg_value); } -- cgit v1.2.3 From 3f8c8497b4fc249e27cb335c627114d8412e584d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 28 Apr 2026 19:11:05 +0200 Subject: hv: utils: replace deprecated strcpy with strscpy in kvp_register strcpy() has been deprecated [1] because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. Use ->body.kvp_register.version directly as the destination buffer and remove the local variable. [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy Signed-off-by: Thorsten Blum Signed-off-by: Wei Liu --- drivers/hv/hv_kvp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 6180ebe040ff..336b278b2182 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -130,18 +131,15 @@ static void kvp_register_done(void) static int kvp_register(int reg_value) { - struct hv_kvp_msg *kvp_msg; - char *version; int ret; kvp_msg = kzalloc_obj(*kvp_msg); if (!kvp_msg) return -ENOMEM; - version = kvp_msg->body.kvp_register.version; kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); + strscpy(kvp_msg->body.kvp_register.version, HV_DRV_VERSION); ret = hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), kvp_register_done); -- cgit v1.2.3 From f1a9e67c11388965802a61922c313bfc43272afe Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:52 -0700 Subject: mshv: limit SynIC management to MSHV-owned resources The SynIC is shared between VMBus and MSHV. VMBus owns the message page (SIMP), event flags page (SIEFP), global enable (SCONTROL), and SINT2. MSHV adds SINT0, SINT5, and the event ring page (SIRBP). Currently mshv_synic_cpu_init() redundantly enables SIMP, SIEFP, and SCONTROL that VMBus already configured, and mshv_synic_cpu_exit() disables all of them. This is wrong because MSHV can be torn down while VMBus is still active. In particular, a kexec reboot notifier tears down MSHV first. Disabling SCONTROL, SIMP, and SIEFP out from under VMBus causes its later cleanup to write SynIC MSRs while SynIC is disabled, which the hypervisor does not tolerate. Restrict MSHV to managing only the resources it owns: - SINT0, SINT5: mask on cleanup, unmask on init - SIRBP: enable/disable as before - SIMP, SIEFP, SCONTROL: leave to VMBus when it is active (L1VH and nested root partition); on a non-nested root partition VMBus does not run, so MSHV must enable/disable them While here, fix the SIEFP and SIRBP memremap() and virt_to_phys() calls to use HV_HYP_PAGE_SHIFT/HV_HYP_PAGE_SIZE instead of PAGE_SHIFT/PAGE_SIZE. The hypervisor always uses 4K pages for SynIC register GPAs regardless of the kernel page size, so using PAGE_SHIFT produces wrong addresses on ARM64 with 64K pages. Note that initialization order matters - VMBUS first, MSHV second, and the reverse on de-init. Ideally, we would want a dedicated SYNIC driver that replaces the cross-dependencies with a clear API and dynamic tracking. Such refactor should go into its own dedicated series, outside of this kexec fix series. Signed-off-by: Jork Loeser Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Stanislav Kinsburskii Signed-off-by: Wei Liu --- drivers/hv/hv.c | 3 + drivers/hv/mshv_synic.c | 150 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae60fd542292..ef4b1b03395d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -272,6 +272,9 @@ void hv_synic_free(void) /* * hv_hyp_synic_enable_regs - Initialize the Synthetic Interrupt Controller * with the hypervisor. + * + * Note: When MSHV is present, mshv_synic_cpu_init() intializes further + * registers later. */ void hv_hyp_synic_enable_regs(unsigned int cpu) { diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..2db3b0192eac 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -456,46 +457,75 @@ static int mshv_synic_cpu_init(unsigned int cpu) union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; union hv_synic_sint sint; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* + * VMBus owns SIMP/SIEFP/SCONTROL when it is active. + * See hv_hyp_synic_enable_regs() for that initialization. + */ + bool vmbus_active = hv_vmbus_exists(); - /* Setup the Synic's message page */ + /* + * Map the SYNIC message page. When VMBus is not active the + * hypervisor pre-provisions the SIMP GPA but may not set + * simp_enabled — enable it here. + */ simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = true; + if (!vmbus_active) { + simp.simp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } *msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT, HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*msg_page)) - return -EFAULT; - - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + goto cleanup_simp; - /* Setup the Synic's event flags page */ + /* + * Map the event flags page. Same as SIMP: enable when + * VMBus is not active, already enabled by VMBus otherwise. + */ siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = true; - *event_flags_page = memremap(siefp.base_siefp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); + if (!vmbus_active) { + siefp.siefp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + } + *event_flags_page = memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*event_flags_page)) - goto cleanup; - - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + goto cleanup_siefp; /* Setup the Synic's event ring page */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); - sirbp.sirbp_enabled = true; - *event_ring_page = memremap(sirbp.base_sirbp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); - if (!(*event_ring_page)) - goto cleanup; + if (hv_root_partition()) { + *event_ring_page = memremap(sirbp.base_sirbp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); + if (!(*event_ring_page)) + goto cleanup_siefp; + } else { + /* + * On L1VH the hypervisor does not provide a SIRBP page. + * Allocate one and program its GPA into the MSR. + */ + *event_ring_page = (struct hv_synic_event_ring_page *) + get_zeroed_page(GFP_KERNEL); + + if (!(*event_ring_page)) + goto cleanup_siefp; + + sirbp.base_sirbp_gpa = virt_to_phys(*event_ring_page) + >> HV_HYP_PAGE_SHIFT; + } + + sirbp.sirbp_enabled = true; hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); if (mshv_sint_irq != -1) @@ -518,28 +548,30 @@ static int mshv_synic_cpu_init(unsigned int cpu) hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX, sint.as_uint64); - /* Enable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 1; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it already enabled SCONTROL. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 1; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; -cleanup: - if (*event_ring_page) { - sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - } - if (*event_flags_page) { +cleanup_siefp: + if (*event_flags_page) + memunmap(*event_flags_page); + if (!vmbus_active) { siefp.siefp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); - memunmap(*event_flags_page); } - if (*msg_page) { +cleanup_simp: + if (*msg_page) + memunmap(*msg_page); + if (!vmbus_active) { simp.simp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - memunmap(*msg_page); } return -EFAULT; @@ -548,16 +580,15 @@ cleanup: static int mshv_synic_cpu_exit(unsigned int cpu) { union hv_synic_sint sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* VMBus owns SIMP/SIEFP/SCONTROL when it is active */ + bool vmbus_active = hv_vmbus_exists(); /* Disable the interrupt */ sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX); @@ -574,28 +605,47 @@ static int mshv_synic_cpu_exit(unsigned int cpu) if (mshv_sint_irq != -1) disable_percpu_irq(mshv_sint_irq); - /* Disable Synic's event ring page */ + /* Disable SYNIC event ring page owned by MSHV */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - /* Disable Synic's event flags page */ - siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + if (hv_root_partition()) { + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + memunmap(*event_ring_page); + } else { + sirbp.base_sirbp_gpa = 0; + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + free_page((unsigned long)*event_ring_page); + } + + /* + * Release our mappings of the message and event flags pages. + * When VMBus is not active, we enabled SIMP/SIEFP — disable + * them. Otherwise VMBus owns the MSRs — leave them. + */ memunmap(*event_flags_page); + if (!vmbus_active) { + union hv_synic_simp simp; + union hv_synic_siefp siefp; - /* Disable Synic's message page */ - simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); + siefp.siefp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + + simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); + simp.simp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } memunmap(*msg_page); - /* Disable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 0; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it owns SCONTROL — leave it. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 0; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; } -- cgit v1.2.3 From efe0fb8c3fe2b996522f7418fd311eeff43c1148 Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:53 -0700 Subject: mshv: clean up SynIC state on kexec for L1VH The reboot notifier that tears down the SynIC cpuhp state guards the cleanup with hv_root_partition(), so on L1VH (where hv_root_partition() is false) SINT0, SINT5, and SIRBP are never cleaned up before kexec. The kexec'd kernel then inherits stale unmasked SINTs and an enabled SIRBP pointing to freed memory. Remove the hv_root_partition() guard so the cleanup runs for all parent partitions. Signed-off-by: Jork Loeser Reviewed-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_synic.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 2db3b0192eac..978a1cace341 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -723,9 +723,6 @@ mshv_unregister_doorbell(u64 partition_id, int doorbell_portid) static int mshv_synic_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { - if (!hv_root_partition()) - return 0; - cpuhp_remove_state(synic_cpuhp_online); return 0; } -- cgit v1.2.3 From 6e55a8d9f21bcb238596bdd8092c92e74c698a3d Mon Sep 17 00:00:00 2001 From: Jork Loeser Date: Mon, 27 Apr 2026 14:38:54 -0700 Subject: mshv: unmap debugfs stats pages on kexec On L1VH, debugfs stats pages are overlay pages: the kernel allocates them and registers the GPAs with the hypervisor via HVCALL_MAP_STATS_PAGE2. These overlay mappings persist in the hypervisor across kexec. If the kexec'd kernel reuses those physical pages, the hypervisor's overlay semantics cause a machine check exception. Fix this by calling mshv_debugfs_exit() from the reboot notifier, which issues HVCALL_UNMAP_STATS_PAGE for each mapped stats page before kexec. This releases the overlay bindings so the physical pages can be safely reused. Guard mshv_debugfs_exit() against being called when init failed. Signed-off-by: Jork Loeser Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Stanislav Kinsburskii Signed-off-by: Wei Liu --- drivers/hv/mshv_debugfs.c | 7 ++++++- drivers/hv/mshv_synic.c | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/mshv_debugfs.c b/drivers/hv/mshv_debugfs.c index 418b6dc8f3c2..3c3e02237ae9 100644 --- a/drivers/hv/mshv_debugfs.c +++ b/drivers/hv/mshv_debugfs.c @@ -674,8 +674,10 @@ int __init mshv_debugfs_init(void) mshv_debugfs = debugfs_create_dir("mshv", NULL); if (IS_ERR(mshv_debugfs)) { + err = PTR_ERR(mshv_debugfs); + mshv_debugfs = NULL; pr_err("%s: failed to create debugfs directory\n", __func__); - return PTR_ERR(mshv_debugfs); + return err; } if (hv_root_partition()) { @@ -710,6 +712,9 @@ remove_mshv_dir: void mshv_debugfs_exit(void) { + if (!mshv_debugfs) + return; + mshv_debugfs_parent_partition_remove(); if (hv_root_partition()) { diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 978a1cace341..88170ce6b83f 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -723,6 +723,7 @@ mshv_unregister_doorbell(u64 partition_id, int doorbell_portid) static int mshv_synic_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { + mshv_debugfs_exit(); cpuhp_remove_state(synic_cpuhp_online); return 0; } -- cgit v1.2.3 From c5c3ef8d49e15d2fc1cec4ad7c91d81b99977440 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 17 Feb 2026 10:23:34 -0800 Subject: Drivers: hv: vmbus: Provide option to skip VMBus unload on panic Currently, VMBus code initiates a VMBus unload in the panic path so that if a kdump kernel is loaded, it can start fresh in setting up its own VMBus connection. However, a driver for the VMBus virtual frame buffer may need to flush dirty portions of the frame buffer back to the Hyper-V host so that panic information is visible in the graphics console. To support such flushing, provide exported functions for the frame buffer driver to specify that the VMBus unload should not be done by the VMBus driver, and to initiate the VMBus unload itself. Together these allow a frame buffer driver to delay the VMBus unload until after it has completed the flush. Ideally, the VMBus driver could use its own panic-path callback to do the unload after all frame buffer drivers have finished. But DRM frame buffer drivers use the kmsg dump callback, and there are no callbacks after that in the panic path. Hence this somewhat messy approach to properly sequencing the frame buffer flush and the VMBus unload. Fixes: 3671f3777758 ("drm/hyperv: Add support for drm_panic") Signed-off-by: Michael Kelley Reviewed-by: Long Li Signed-off-by: Wei Liu --- drivers/hv/channel_mgmt.c | 1 + drivers/hv/hyperv_vmbus.h | 1 - drivers/hv/vmbus_drv.c | 25 ++++++++++++++++++------- include/linux/hyperv.h | 3 +++ 4 files changed, 22 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 84eb0a6a0b54..89d214dda360 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -952,6 +952,7 @@ void vmbus_initiate_unload(bool crash) else vmbus_wait_for_unload(); } +EXPORT_SYMBOL_GPL(vmbus_initiate_unload); static void vmbus_setup_channel_state(struct vmbus_channel *channel, struct vmbus_channel_offer_channel *offer) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 05a36854389a..eb8bdd8bb1f5 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -441,7 +441,6 @@ void hv_vss_deinit(void); int hv_vss_pre_suspend(void); int hv_vss_pre_resume(void); void hv_vss_onchannelcallback(void *context); -void vmbus_initiate_unload(bool crash); static inline void hv_poll_channel(struct vmbus_channel *channel, void (*cb)(void *)) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d28ff45d4cfd..c9eeb2ec365d 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -69,19 +69,29 @@ bool vmbus_is_confidential(void) } EXPORT_SYMBOL_GPL(vmbus_is_confidential); +static bool skip_vmbus_unload; + +/* + * Allow a VMBus framebuffer driver to specify that in the case of a panic, + * it will do the VMbus unload operation once it has flushed any dirty + * portions of the framebuffer to the Hyper-V host. + */ +void vmbus_set_skip_unload(bool skip) +{ + skip_vmbus_unload = skip; +} +EXPORT_SYMBOL_GPL(vmbus_set_skip_unload); + /* * The panic notifier below is responsible solely for unloading the * vmbus connection, which is necessary in a panic event. - * - * Notice an intrincate relation of this notifier with Hyper-V - * framebuffer panic notifier exists - we need vmbus connection alive - * there in order to succeed, so we need to order both with each other - * [see hvfb_on_panic()] - this is done using notifiers' priorities. */ static int hv_panic_vmbus_unload(struct notifier_block *nb, unsigned long val, void *args) { - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); + return NOTIFY_DONE; } static struct notifier_block hyperv_panic_vmbus_unload_block = { @@ -2897,7 +2907,8 @@ static void hv_crash_handler(struct pt_regs *regs) { int cpu; - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); /* * In crash handler we can't schedule synic cleanup for all CPUs, * doing the cleanup for current CPU only. This should be sufficient diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 964f1be8150c..41a3d82f0722 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1336,6 +1336,9 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, bool fb_overlap_ok); void vmbus_free_mmio(resource_size_t start, resource_size_t size); +void vmbus_initiate_unload(bool crash); +void vmbus_set_skip_unload(bool skip); + /* * GUID definitions of various offer types - services offered to the guest. */ -- cgit v1.2.3 From 8b35874f56ded0cc1a90a25b87411249a86246cd Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 17 Feb 2026 10:23:35 -0800 Subject: drm/hyperv: During panic do VMBus unload after frame buffer is flushed In a VM, Linux panic information (reason for the panic, stack trace, etc.) may be written to a serial console and/or a virtual frame buffer for a graphics console. The latter may need to be flushed back to the host hypervisor for display. The current Hyper-V DRM driver for the frame buffer does the flushing *after* the VMBus connection has been unloaded, such that panic messages are not displayed on the graphics console. A user with a Hyper-V graphics console is left with just a hung empty screen after a panic. The enhanced control that DRM provides over the panic display in the graphics console is similarly non-functional. Commit 3671f3777758 ("drm/hyperv: Add support for drm_panic") added the Hyper-V DRM driver support to flush the virtual frame buffer. It provided necessary functionality but did not handle the sequencing problem with VMBus unload. Fix the full problem by using VMBus functions to suppress the VMBus unload that is normally done by the VMBus driver in the panic path. Then after the frame buffer has been flushed, do the VMBus unload so that a kdump kernel can start cleanly. As expected, CONFIG_DRM_PANIC must be selected for these changes to have effect. As a side benefit, the enhanced features of the DRM panic path are also functional. Fixes: 3671f3777758 ("drm/hyperv: Add support for drm_panic") Signed-off-by: Michael Kelley Reviewed-by: Jocelyn Falempe Signed-off-by: Wei Liu --- drivers/gpu/drm/hyperv/hyperv_drm_drv.c | 5 +++++ drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 15 ++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 06b5d96e6eaf..b6bf6412ae34 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -150,6 +150,10 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, goto err_free_mmio; } + /* If DRM panic path is stubbed out VMBus code must do the unload */ + if (IS_ENABLED(CONFIG_DRM_PANIC)) + vmbus_set_skip_unload(true); + drm_client_setup(dev, NULL); return 0; @@ -169,6 +173,7 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) struct drm_device *dev = hv_get_drvdata(hdev); struct hyperv_drm_device *hv = to_hv(dev); + vmbus_set_skip_unload(false); drm_dev_unplug(dev); drm_atomic_helper_shutdown(dev); vmbus_close(hdev->channel); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 7978f8c8108c..d48ca6c23b7c 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -212,15 +212,16 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane) struct hyperv_drm_device *hv = to_hv(plane->dev); struct drm_rect rect; - if (!plane->state || !plane->state->fb) - return; + if (plane->state && plane->state->fb) { + rect.x1 = 0; + rect.y1 = 0; + rect.x2 = plane->state->fb->width; + rect.y2 = plane->state->fb->height; - rect.x1 = 0; - rect.y1 = 0; - rect.x2 = plane->state->fb->width; - rect.y2 = plane->state->fb->height; + hyperv_update_dirt(hv->hdev, &rect); + } - hyperv_update_dirt(hv->hdev, &rect); + vmbus_initiate_unload(true); } static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { -- cgit v1.2.3 From 29d87434cb91b7689de2917830ca82acfd2770f5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:19:37 +0200 Subject: regulator: mt6363: select CONFIG_IRQ_DOMAIN When build-testing this driver without CONFIG_IRQ_DOMAIN causes a compile-time error: drivers/regulator/mt6363-regulator.c: In function 'mt6363_regulator_probe': drivers/regulator/mt6363-regulator.c:884:18: error: implicit declaration of function 'irq_find_host' [-Wimplicit-function-declaration] 884 | domain = irq_find_host(interrupt_parent); | ^~~~~~~~~~~~~ drivers/regulator/mt6363-regulator.c:884:16: error: assignment to 'struct irq_domain *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 884 | domain = irq_find_host(interrupt_parent); | ^ drivers/regulator/mt6363-regulator.c:896:30: error: implicit declaration of function 'irq_create_fwspec_mapping'; did you mean 'irq_create_of_mapping'? [-Wimplicit-function-declaration] 896 | info->virq = irq_create_fwspec_mapping(&fwspec); | ^~~~~~~~~~~~~~~~~~~~~~~~~ | irq_create_of_mapping This is rather hard to trigger because so many other drivers enable IRQ_DOMAIN already, but I ran into this on an s390 randconfig build. Ensure this is always enabled using a Kconfig 'select IRQ_DOMAIN' entry, as we do for all other users of this. Fixes: 3c36965df808 ("regulator: Add support for MediaTek MT6363 SPMI PMIC Regulators") Signed-off-by: Arnd Bergmann Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260526102003.2527570-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 78076ac6eac4..87554ab92801 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -977,6 +977,7 @@ config REGULATOR_MT6363 tristate "MT6363 SPMI PMIC regulator driver" depends on SPMI select REGMAP_SPMI + select IRQ_DOMAIN help Say Y here to enable support for regulators found in the MediaTek MT6363 SPMI PMIC. -- cgit v1.2.3 From 3cd07ee35a66038fd1a643632bfc057645e07c9a Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 26 May 2026 10:21:31 +0800 Subject: cpufreq/amd-pstate: drop stale @epp_cached kdoc Commit 4e16c1175238 ("cpufreq/amd-pstate: Stop caching EPP") removed the epp_cached field from struct amd_cpudata in favour of always reading from cppc_req_cached, but the kdoc above the struct still documents @epp_cached. Drop the now-stale @epp_cached entry. Reviewed-by: Mario Limonciello (AMD) Fixes: 4e16c1175238 ("cpufreq/amd-pstate: Stop caching EPP") Signed-off-by: Zhan Xusheng Link: https://lore.kernel.org/r/20260526022131.1302373-1-zhanxusheng@xiaomi.com Signed-off-by: Mario Limonciello (AMD) --- drivers/cpufreq/amd-pstate.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index e4722e54387b..23e8baa05849 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -84,7 +84,6 @@ struct amd_aperf_mperf { * @hw_prefcore: check whether HW supports preferred core featue. * Only when hw_prefcore and early prefcore param are true, * AMD P-State driver supports preferred core featue. - * @epp_cached: Cached CPPC energy-performance preference value * @policy: Cpufreq policy value * @suspended: If CPU core if offlined * @epp_default_ac: Default EPP value for AC power source -- cgit v1.2.3 From 840b740a35bf969734e0f2e44c21289fdd03079e Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Tue, 26 May 2026 07:13:04 -0700 Subject: mshv: Add conditional VMBus dependency When the VMBus driver is not part of the kernel (CONFIG_HYPERV_VMBUS=n), the MSHV root driver fails to link: ERROR: modpost: "hv_vmbus_exists" [drivers/hv/mshv_root.ko] undefined! Fix this while meeting these requirements: * It must be possible to include the MSHV root driver without the VMBus driver. In such case, the MSHV root driver can be built-in to the kernel image, or it can be built as a separate module. * If both the MSHV root driver and the VMBus driver are present, the MSHV root driver and VMBus driver can both be built-in, or they can both be separate modules. Or the MSHV root driver can be a module while the VMBus driver can be built-in, but the reverse is disallowed. Regardless of the build choices, the VMBus driver must be loaded before the MSHV driver in order for the SynIC to be managed properly (see comments in the MSHV SynIC code). The fix has two parts: * Add a Kconfig entry for MSHV_ROOT to depend on HYPERV_VMBUS if HYPERV_VMBUS is present. The entry disallows MSHV_ROOT being built-in when HYPERV_VMBUS is a module, but without requiring that HYPERV_VMBUS be built. * Add a stub implementation of hv_vmbus_exists() for when the VMBus driver is not present so that the MSHV root driver has no module dependency on VMBus. When the VMBus driver *is* present, the module dependency ensures that the VMBus driver loads first when both are built as modules. Existing code ensures that the VMBus driver loads first if it is built-in. The VMBus driver uses subsys_initcall(), which is initcall level 4. The MSHV root driver uses module_init(), which becomes device_init() when built-in, and device_init() is initcall level 6. Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/all/20260520074044.923728-1-arnd@kernel.org/ Signed-off-by: Michael Kelley Acked-by: Arnd Bergmann Reviewed-by: Jork Loeser Reviewed-by: Hardik Garg Signed-off-by: Wei Liu --- drivers/hv/Kconfig | 1 + include/linux/hyperv.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 2d0b3fcb0ff8..aa11bcefddf2 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -74,6 +74,7 @@ config MSHV_ROOT # e.g. When withdrawing memory, the hypervisor gives back 4k pages in # no particular order, making it impossible to reassemble larger pages depends on PAGE_SIZE_4KB + depends on HYPERV_VMBUS if HYPERV_VMBUS select EVENTFD select VIRT_XFER_TO_GUEST_WORK select HMM_MIRROR diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 41a3d82f0722..734b7ef98f4d 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1304,7 +1304,11 @@ static inline void *hv_get_drvdata(struct hv_device *dev) struct device *hv_get_vmbus_root_device(void); +#if IS_ENABLED(CONFIG_HYPERV_VMBUS) bool hv_vmbus_exists(void); +#else +static inline bool hv_vmbus_exists(void) { return false; } +#endif struct hv_ring_buffer_debug_info { u32 current_interrupt_mask; -- cgit v1.2.3 From c53763aa2b9c96fbabee68ebe1e13074cb09bfb2 Mon Sep 17 00:00:00 2001 From: Can Peng Date: Wed, 20 May 2026 15:16:32 +0800 Subject: mshv: use kmalloc_array in mshv_root_scheduler_init Replace kmalloc() with kmalloc_array() to prevent potential overflow, as recommended in Documentation/process/deprecated.rst. No functional change. Signed-off-by: Can Peng Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index bd1359eb58dd..146726cc4e9b 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -2241,7 +2241,7 @@ static int mshv_root_scheduler_init(unsigned int cpu) outputarg = (void **)this_cpu_ptr(root_scheduler_output); /* Allocate two consecutive pages. One for input, one for output. */ - p = kmalloc(2 * HV_HYP_PAGE_SIZE, GFP_KERNEL); + p = kmalloc_array(2, HV_HYP_PAGE_SIZE, GFP_KERNEL); if (!p) return -ENOMEM; -- cgit v1.2.3 From 016a25e4b0df4d77e7c258edee4aaf982e4ee809 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 7 May 2026 14:28:38 -0700 Subject: Drivers: hv: vmbus: Improve the logic of reserving fb_mmio on Gen2 VMs If vmbus_reserve_fb() in the kdump/kexec kernel fails to properly reserve the framebuffer MMIO range (which is below 4GB) due to a Gen2 VM's screen.lfb_base being zero [1], there is an MMIO conflict between the drivers hyperv-drm and pci-hyperv: when the driver pci-hyperv's hv_allocate_config_window() calls vmbus_allocate_mmio() to get an MMIO range, typically it gets a 32-bit MMIO range that overlaps with the framebuffer MMIO range, and later hv_pci_enter_d0() fails with an error message "PCI Pass-through VSP failed D0 Entry with status" since the host thinks that PCI devices must not use MMIO space that the host has assigned to the framebuffer. This is especially an issue if pci-hyperv is built-in and hyperv-drm is built as a module. Consequently, the kdump/kexec kernel fails to detect PCI devices via pci-hyperv, and may fail to mount the root file system, which may reside in a NVMe disk. The issue described here has existed for SR-IOV VF NICs since day one of the pci-hyperv driver, and has been worked around on x64 when possible. With the recent introduction of ARM64 VMs that boot from NVMe, there is no workaround, so we need a formal fix. On Gen2 VMs, if the screen.lfb_base is 0 in the kdump/kexec kernel [1], fall back to the low MMIO base, which should be equal to the framebuffer MMIO base [2] (the statement is true according to my testing on x64 Windows Server 2016, and on x64 and ARM64 Windows Server 2025 and on Azure. I checked with the Hyper-V team and they said the statement should continue to be true for Gen2 VMs). In the first kernel, screen.lfb_base is not 0; if the user specifies a very high resolution, it's not enough to only reserve 8MB: let's always reserve half of the space below 4GB, but cap the reservation to 128MB, which is the required framebuffer size of the highest resolution 7680*4320 supported by Hyper-V. While at it, fix the comparison "end > VTPM_BASE_ADDRESS" by changing the > to >=. Here the 'end' is an inclusive end (typically, it's 0xFFFF_FFFF for the low MMIO range). Note: vmbus_reserve_fb() now also reserves an MMIO range at the beginning of the low MMIO range on CVMs, which have no framebuffers (the 'screen.lfb_base' in vmbus_reserve_fb() is 0 for CVMs), just in case the host might treat the beginning of the low MMIO range specially [3]. BTW, the OpenHCL kernel is not affected by the change, because that kernel boots with DeviceTree rather than ACPI (so vmbus_reserve_fb() won't run there), and there is no framebuffer device for that kernel. Note: normally Gen1 VMs don't have the MMIO conflict issue because the framebuffer MMIO range (which is hardcoded to base=4GB-128MB and size=64MB for Gen1 VMs by the host) is always reported via the legacy PCI graphics device's BAR, so the kdump/kexec kernel can reserve the 64MB MMIO range; however, if the VM is configured to use a very high resolution and the required framebuffer size exceeds 64MB (AFAIK, in practice, this isn't a typical configuration by users), the hyperv-drm driver may need to allocate an MMIO range above 4GB and change the framebuffer MMIO location to the allocated MMIO range -- in this case, there can still be issues [4] which can't be easily fixed: any possible affected Gen1 users would have to use a resolution whose framebuffer size is <= 64MB, or switch to Gen2 VMs. [1] https://lore.kernel.org/all/SA1PR21MB692176C1BC53BFC9EAE5CF8EBF51A@SA1PR21MB6921.namprd21.prod.outlook.com/ [2] https://lore.kernel.org/all/SA1PR21MB69218F955B62DFF62E3E88D2BF222@SA1PR21MB6921.namprd21.prod.outlook.com/ [3] https://lore.kernel.org/all/SN6PR02MB415726B17D5A6027CD1717E8D4342@SN6PR02MB4157.namprd02.prod.outlook.com/ [4] https://lore.kernel.org/all/SA1PR21MB69213486F821CA5A2C793C81BF342@SA1PR21MB6921.namprd21.prod.outlook.com/ Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") CC: stable@vger.kernel.org Reviewed-by: Michael Kelley Tested-by: Krister Johansen Tested-by: Matthew Ruffell Signed-off-by: Dexuan Cui Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index c9eeb2ec365d..b80a35c778ab 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2327,8 +2327,8 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) return AE_NO_MEMORY; /* If this range overlaps the virtual TPM, truncate it. */ - if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) - end = VTPM_BASE_ADDRESS; + if (end >= VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) + end = VTPM_BASE_ADDRESS - 1; new_res->name = "hyperv mmio"; new_res->flags = IORESOURCE_MEM; @@ -2395,6 +2395,7 @@ static void vmbus_mmio_remove(void) static void __maybe_unused vmbus_reserve_fb(void) { resource_size_t start = 0, size; + resource_size_t low_mmio_base; struct pci_dev *pdev; if (efi_enabled(EFI_BOOT)) { @@ -2402,6 +2403,24 @@ static void __maybe_unused vmbus_reserve_fb(void) if (IS_ENABLED(CONFIG_SYSFB)) { start = sysfb_primary_display.screen.lfb_base; size = max_t(__u32, sysfb_primary_display.screen.lfb_size, 0x800000); + + low_mmio_base = hyperv_mmio->start; + if (!low_mmio_base || upper_32_bits(low_mmio_base) || + (start && start < low_mmio_base)) { + pr_warn("Unexpected low mmio base %pa\n", &low_mmio_base); + } else { + /* + * If the kdump/kexec or CVM kernel's lfb_base + * is 0, fall back to the low mmio base. + */ + if (!start) + start = low_mmio_base; + /* + * Reserve half of the space below 4GB for high + * resolutions, but cap the reservation to 128MB. + */ + size = min((SZ_4G - start) / 2, SZ_128M); + } } } else { /* Gen1 VM: get FB base from PCI */ @@ -2422,8 +2441,10 @@ static void __maybe_unused vmbus_reserve_fb(void) pci_dev_put(pdev); } - if (!start) + if (!start) { + pr_warn("Unexpected framebuffer mmio base of zero\n"); return; + } /* * Make a claim for the frame buffer in the resource tree under the @@ -2433,6 +2454,8 @@ static void __maybe_unused vmbus_reserve_fb(void) */ for (; !fb_mmio && (size >= 0x100000); size >>= 1) fb_mmio = __request_region(hyperv_mmio, start, size, fb_mmio_name, 0); + + pr_info("hv_mmio=%pR,%pR fb=%pR\n", hyperv_mmio, hyperv_mmio->sibling, fb_mmio); } /** -- cgit v1.2.3 From 98e0fc32e53dd62cd38a0d67eaf5846ae20078cc Mon Sep 17 00:00:00 2001 From: "Anirudh Rayabharam (Microsoft)" Date: Wed, 13 May 2026 13:25:56 +0000 Subject: mshv: support 1G hugepages by passing them as 2M-aligned chunks The hypervisor's map GPA hypercall coalesces contiguous 2M-aligned chunks into 1G mappings when alignment permits, so the driver can support 1G hugepages by feeding them in as 2M chunks. Note that this is the only way to make 1G mappings; there is no way to directly map a 1G hugepage using the hypercall. Always emit a 2M (PMD_ORDER) stride for the huge-page case. The hypercall has no 1G stride, so 1G folios are processed as a sequence of 2M chunks. Folios whose order is less than PMD_ORDER (e.g. mTHP) fall back to single-page stride; mapping them as 2M would fail in the hypervisor anyway. Assisted-by: Copilot-CLI:claude-opus-4.7 Signed-off-by: Anirudh Rayabharam (Microsoft) Acked-by: Stanislav Kinsburskii Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_regions.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c index fdffd4f002f6..6d65e5b42152 100644 --- a/drivers/hv/mshv_regions.c +++ b/drivers/hv/mshv_regions.c @@ -29,29 +29,27 @@ * Uses huge page stride if the backing page is huge and the guest mapping * is properly aligned; otherwise falls back to single page stride. * - * Return: Stride in pages, or -EINVAL if page order is unsupported. + * Return: Stride in pages. */ -static int mshv_chunk_stride(struct page *page, - u64 gfn, u64 page_count) +static unsigned int mshv_chunk_stride(struct page *page, u64 gfn, + u64 page_count) { - unsigned int page_order; + unsigned int page_order = folio_order(page_folio(page)); /* * Use single page stride by default. For huge page stride, the - * page must be compound and point to the head of the compound - * page, and both gfn and page_count must be huge-page aligned. + * folio order must be at least PMD_ORDER, the page's PFN must be + * 2M-aligned (so that a 2M-aligned tail page of a larger folio is + * acceptable), and both gfn and page_count must be 2M-aligned. */ - if (!PageCompound(page) || !PageHead(page) || + if (page_order < PMD_ORDER || + !IS_ALIGNED(page_to_pfn(page), PTRS_PER_PMD) || !IS_ALIGNED(gfn, PTRS_PER_PMD) || !IS_ALIGNED(page_count, PTRS_PER_PMD)) return 1; - page_order = folio_order(page_folio(page)); - /* The hypervisor only supports 2M huge page */ - if (page_order != PMD_ORDER) - return -EINVAL; - - return 1 << page_order; + /* Use 2M stride always i.e. process 1G folios as 2M chunks */ + return 1 << PMD_ORDER; } /** @@ -86,15 +84,14 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region, u64 gfn = region->start_gfn + page_offset; u64 count; struct page *page; - int stride, ret; + unsigned int stride; + int ret; page = region->mreg_pages[page_offset]; if (!page) return -EINVAL; stride = mshv_chunk_stride(page, gfn, page_count); - if (stride < 0) - return stride; /* Start at stride since the first stride is validated */ for (count = stride; count < page_count; count += stride) { -- cgit v1.2.3 From 42445de1765547f56f48d107c0b8f3482c98458e Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Fri, 29 May 2026 12:36:02 -0700 Subject: Revert "drm/xe/nvls: Define GuC firmware for NVL-S" This reverts commit 4e88de313ff4d1c67b644b1f39f9fb4089711b71. The early GuC FW definition meant for our CI branch was accidentally merged to the drm-xe-next branch instead. This GuC FW will never be released to linux-firmware, so we do not want the definition to be available in the mainline Linux codebase. Fixes: 4e88de313ff4 ("drm/xe/nvls: Define GuC firmware for NVL-S") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Cc: Rodrigo Vivi Cc: Matt Roper Cc: stable@vger.kernel.org # v7.0+ Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260529193558.185436-11-daniele.ceraolospurio@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 65b8e0ac86e48cfc9128c04dfc53ea3395d030dd) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_uc_fw.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index 9cebb2490245..18ebefd444fe 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,7 +115,6 @@ struct fw_blobs_by_type { #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ -- cgit v1.2.3 From abf888b03a9805a3bc37948a0df443553b1c0910 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sat, 30 May 2026 15:37:42 -0300 Subject: drm/v3d: Wait for pending L2T flush before cleaning caches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_clean_caches() starts the cache-clean sequence by writing V3D_L2TCACTL_TMUWCF to V3D_CTL_L2TCACTL and then polling for that bit to clear. It does not, however, check for an L2T flush (L2TFLS) that may still be in flight from a previous operation. On pre-V3D 7.1 hardware, kicking off the TMU write-combiner flush while an L2T flush is still pending can clobber bits in L2TCACTL and cause cache inconsistencies. Poll for L2TFLS to clear before writing L2TCACTL on V3D < 7.1, ensuring any pending flush has completed before a new clean is issued. Cc: stable@vger.kernel.org Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") Link: https://patch.msgid.link/20260530-v3d-fix-rpi4-freezes-v1-1-c2c8307da6ce@igalia.com Signed-off-by: Maíra Canal Reviewed-by: Iago Toral Quiroga --- drivers/gpu/drm/v3d/v3d_gem.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 75d9eccd7966..dd7da419702f 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -213,6 +213,14 @@ v3d_clean_caches(struct v3d_dev *v3d) trace_v3d_cache_clean_begin(dev); + /* GFXH-1897: Ensure pending flushes complete before writing L2TCACTL */ + if (v3d->ver < V3D_GEN_71) { + if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & + V3D_L2TCACTL_L2TFLS), 100)) { + drm_err(dev, "Timeout waiting for L2T clean\n"); + } + } + V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & V3D_L2TCACTL_TMUWCF), 100)) { -- cgit v1.2.3 From 4a1b1ac2744694a2ecd66a84bdb1445f4ef24bee Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 26 May 2026 12:40:25 -0300 Subject: RDMA/core: Validate the passed in fops for ib_get_ucaps() Sashiko pointed out it is not safe to rely only on the devt because char/block alias so if the user finds a block device with the same dev_t it can masquerade as a ucap cdev fd. Test the f_ops to only accept authentic cdevs. Link: https://patch.msgid.link/r/0-v1-fd9482545e37+1e25-ib_ucaps_fd_ops_jgg@nvidia.com Cc: stable@vger.kernel.org Fixes: 61e51682816d ("RDMA/uverbs: Introduce UCAP (User CAPabilities) API") Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ucaps.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/ucaps.c b/drivers/infiniband/core/ucaps.c index 948093260dbd..5155ff0e538e 100644 --- a/drivers/infiniband/core/ucaps.c +++ b/drivers/infiniband/core/ucaps.c @@ -82,14 +82,12 @@ static int get_ucap_from_devt(dev_t devt, u64 *idx_mask) static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev) { - struct file *file; + CLASS(fd, f)(fd); - file = fget(fd); - if (!file) + if (fd_empty(f) || fd_file(f)->f_op != &ucaps_cdev_fops) return -EBADF; - *ret_dev = file_inode(file)->i_rdev; - fput(file); + *ret_dev = file_inode(fd_file(f))->i_rdev; return 0; } -- cgit v1.2.3 From 1d0b597facdd3c0239c88e8797c1014e1ea0ef15 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 14:08:53 +0200 Subject: accel/ivpu: Add bounds check for firmware runtime memory Validate that the firmware runtime memory specified in the image header is properly aligned and sized to hold the firmware image. This prevents errors during memory allocation and image transfer. Fixes: 2007e210b6a1 ("accel/ivpu: Split FW runtime and global memory buffers") Cc: stable@vger.kernel.org # v7.0+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529120853.135876-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_fw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 107f8ad31050..33c50779c06b 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -259,6 +259,22 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) return -EINVAL; } + if (!PAGE_ALIGNED(runtime_addr)) { + ivpu_err(vdev, "Runtime address 0x%llx not page aligned\n", runtime_addr); + return -EINVAL; + } + + if (!PAGE_ALIGNED(runtime_size)) { + ivpu_err(vdev, "Runtime size %llu not page aligned\n", runtime_size); + return -EINVAL; + } + + if (runtime_size < image_size) { + ivpu_err(vdev, "Runtime size too small: %llu, image size: %llu\n", + runtime_size, image_size); + return -EINVAL; + } + if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) { ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n", image_load_addr, image_size); -- cgit v1.2.3 From dd1311bcf0e62f0c515115f46a3813370f4a4bb1 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 13:58:42 +0200 Subject: accel/ivpu: Add bounds checks for firmware log indices Add validation that read and write indices in the firmware log buffer are within valid bounds (< data_size) before using them. If out-of-bounds indices are encountered (from firmware), clamp them to safe values instead of proceeding with invalid offsets. This prevents potential out-of-bounds buffer access when firmware supplies invalid log indices. Fixes: 1fc1251149a7 ("accel/ivpu: Refactor functions in ivpu_fw_log.c") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529115842.135378-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_fw_log.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 337c906b0210..275baf844b56 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -98,6 +98,11 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0; u32 log_end = READ_ONCE(log->write_index); + if (log_start >= data_size) + log_start = 0; + if (log_end > data_size) + log_end = data_size; + if (log->wrap_count == log->read_wrap_count) { if (log_end <= log_start) { drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name); -- cgit v1.2.3 From fb176425837693f50c5c9fc8db6fbb04af22bd0a Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Fri, 29 May 2026 14:08:41 +0200 Subject: accel/ivpu: Add buffer overflow check in MS get_info_ioctl Add validation that the info size returned from the metric stream info query is not exceeded when checked against the allocated buffer size. If the firmware returns a size larger than the buffer, reject the operation with -EOVERFLOW instead of proceeding with an incorrect buffer copy. Fixes: cdfad4db7756 ("accel/ivpu: Add NPU profiling support") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260529120841.135852-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_ms.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/accel/ivpu/ivpu_ms.c b/drivers/accel/ivpu/ivpu_ms.c index be43851f5f32..cd176e77b9a0 100644 --- a/drivers/accel/ivpu/ivpu_ms.c +++ b/drivers/accel/ivpu/ivpu_ms.c @@ -291,6 +291,13 @@ int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file * if (ret) goto unlock; + if (info_size > ivpu_bo_size(bo)) { + ivpu_warn_ratelimited(vdev, "MS info overflow: %#llx > %#zx\n", + info_size, ivpu_bo_size(bo)); + ret = -EOVERFLOW; + goto unlock; + } + if (args->buffer_size < info_size) { ret = -ENOSPC; goto unlock; -- cgit v1.2.3 From ae0383e5a9a4b12d68c76c4769857def4665deff Mon Sep 17 00:00:00 2001 From: Yicong Hui Date: Mon, 6 Apr 2026 19:00:13 +0100 Subject: drm/imx: Fix three kernel-doc warnings in dcss-scaler.c Fix the following W=1 kerneldoc warnings by adding the missing parameter descriptions for @phase0_identity and @nn_interpolation in dcss_scaler_filter_design() and @phase0_identity in dcss_scaler_gaussian_filter() Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:173 function parameter 'phase0_identity' not described in 'dcss_scaler_gaussian_filter' Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'phase0_identity' not described in 'dcss_scaler_filter_design' Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'nn_interpolation' not described in 'dcss_scaler_filter_design' Fixes: 9021c317b770 ("drm/imx: Add initial support for DCSS on iMX8MQ") Signed-off-by: Yicong Hui Reviewed-by: Laurentiu Palcu Link: https://patch.msgid.link/20260406180013.2442096-1-yiconghui@gmail.com Signed-off-by: Liu Ying --- drivers/gpu/drm/imx/dcss/dcss-scaler.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c index 32c3f46b21da..5c7f8d952ec1 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -166,6 +166,7 @@ static int exp_approx_q(int x) * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] * @use_5_taps: indicates whether to use 5 taps or 7 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output filter coefficients */ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, @@ -262,7 +263,9 @@ static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps, * @src_length: length of input * @dst_length: length of output * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output coefficients + * @nn_interpolation: whether to use nearest neighbor instead of gaussian filter */ static void dcss_scaler_filter_design(int src_length, int dst_length, bool use_5_taps, bool phase0_identity, -- cgit v1.2.3 From 5629eec1a2829871d496f3042884cdc267612f6a Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Sat, 30 May 2026 17:04:34 +0200 Subject: cpufreq/amd-pstate: Fix setting EPP in performance mode EPP 0 is the only supported value in the performance policy. commit 798c47593cca ("cpufreq/amd-pstate: Add support for platform profile class") changed this while adding platform profile support to the dynamic EPP feature, but this actually wasn't necessary since platform profile writes disable manual EPP writes. Restore allowing writing EPP of 0 when in performance mode. Reviewed-by: Marco Scardovi Tested-by: Marco Scardovi Reported-by: Stuart Meckle Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221473 Closes: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/work_items/190 Fixes: 798c47593cca ("cpufreq/amd-pstate: Add support for platform profile class") Signed-off-by: Mario Limonciello (AMD) --- drivers/cpufreq/amd-pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 62b5d995281d..72df461e7b39 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1428,7 +1428,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, epp = cpudata->epp_default_dc; } - if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); return -EBUSY; } -- cgit v1.2.3 From b230b57bd6f242aaac3b8e62c7d18c69e1e30392 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Tue, 26 May 2026 08:15:36 +0000 Subject: RDMA/efa: Validate SQ ring size against max LLQ size Validate the SQ ring size against the device's max LLQ size. This ensures that when using 128-byte WQEs, userspace cannot exceed the queue limits. On create QP, userspace provides the SQ ring size (depth x WQE size) which is validated against the max LLQ size. Fixes: 40909f664d27 ("RDMA/efa: Add EFA verbs implementation") Link: https://patch.msgid.link/r/20260526081536.1203553-1-ynachum@amazon.com Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/efa/efa_verbs.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 7bd0838ebc99..9b2b652800e4 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -613,7 +613,8 @@ err_remove_mmap: } static int efa_qp_validate_cap(struct efa_dev *dev, - struct ib_qp_init_attr *init_attr) + struct ib_qp_init_attr *init_attr, + u32 sq_ring_size) { if (init_attr->cap.max_send_wr > dev->dev_attr.max_sq_depth) { ibdev_dbg(&dev->ibdev, @@ -622,6 +623,14 @@ static int efa_qp_validate_cap(struct efa_dev *dev, dev->dev_attr.max_sq_depth); return -EINVAL; } + + if (sq_ring_size > dev->dev_attr.max_llq_size) { + ibdev_dbg(&dev->ibdev, + "qp: requested sq ring size[%u] exceeds the max[%u]\n", + sq_ring_size, dev->dev_attr.max_llq_size); + return -EINVAL; + } + if (init_attr->cap.max_recv_wr > dev->dev_attr.max_rq_depth) { ibdev_dbg(&dev->ibdev, "qp: requested receive wr[%u] exceeds the max[%u]\n", @@ -691,14 +700,6 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, ucontext = rdma_udata_to_drv_context(udata, struct efa_ucontext, ibucontext); - err = efa_qp_validate_cap(dev, init_attr); - if (err) - goto err_out; - - err = efa_qp_validate_attr(dev, init_attr); - if (err) - goto err_out; - err = ib_copy_validate_udata_in_cm(udata, cmd, driver_qp_type, 0); if (err) goto err_out; @@ -720,6 +721,14 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, goto err_out; } + err = efa_qp_validate_cap(dev, init_attr, cmd.sq_ring_size); + if (err) + goto err_out; + + err = efa_qp_validate_attr(dev, init_attr); + if (err) + goto err_out; + create_qp_params.uarn = ucontext->uarn; create_qp_params.pd = to_epd(ibqp->pd)->pdn; -- cgit v1.2.3 From ad0979fe053e9f2db82da82188256ef6eb41095a Mon Sep 17 00:00:00 2001 From: Zeyu WANG Date: Wed, 3 Jun 2026 01:09:09 +0800 Subject: Input: atkbd - add DMI quirk for Lenovo Yoga Air 14 (83QK) The Lenovo Yoga Air 14 (83QK) laptop keyboard becomes unresponsive after the standard atkbd init sequence. Controlled testing on the actual hardware shows the F5 (ATKBD_CMD_RESET_DIS / deactivate) command specifically corrupts the EC state, causing zero IRQ1 interrupts after init. Skipping only the deactivate command (while keeping F4 ENABLE) resolves the issue completely: both keystroke input and CapsLock LED toggle work correctly. The reverse test - skipping only F4 while keeping F5 - makes the problem worse (zero keystroke interrupts), confirming F5 is the sole culprit. Add a DMI quirk entry for LENOVO/83QK using the existing atkbd_deactivate_fixup callback, consistent with the existing entries for LG Electronics and HONOR FMB-P that address the same EC F5 deactivate issue. Signed-off-by: Zeyu WANG Link: https://patch.msgid.link/20260602170909.14725-1-zeyu.thomas.wang@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index c8ad55f26ea8..217e66ee36a1 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1923,6 +1923,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; -- cgit v1.2.3 From 26eb7c0a7ab09d83eec833db6a5a2bc60b9d4d9a Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Mon, 1 Jun 2026 13:59:53 +0530 Subject: drm/i915: Fix color blob reference handling in intel_plane_state Take proper references for hw color blobs (degamma_lut, gamma_lut, ctm, lut_3d) in intel_plane_duplicate_state() and drop them in intel_plane_destroy_state(). v2: - handle blobs in hw state clear Cc: #v6.19+ Fixes: 3b7476e786c2 ("drm/i915/color: Add framework to program PRE/POST CSC LUT") Fixes: a78f1b6baf4d ("drm/i915/color: Add framework to program CSC") Fixes: 65db7a1f9cf7 ("drm/i915/color: Add 3D LUT to color pipeline") Reviewed-by: Pranay Samala #v1 Reviewed-by: Uma Shankar Signed-off-by: Chaitanya Kumar Borah Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260601082953.128539-4-chaitanya.kumar.borah@intel.com (cherry picked from commit c6eea1925154b6697fe22b217faab9bb30635e6b) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_plane.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 82f445c83158..07eae4176dad 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -144,6 +144,15 @@ intel_plane_duplicate_state(struct drm_plane *plane) if (intel_state->hw.fb) drm_framebuffer_get(intel_state->hw.fb); + if (intel_state->hw.degamma_lut) + drm_property_blob_get(intel_state->hw.degamma_lut); + if (intel_state->hw.gamma_lut) + drm_property_blob_get(intel_state->hw.gamma_lut); + if (intel_state->hw.ctm) + drm_property_blob_get(intel_state->hw.ctm); + if (intel_state->hw.lut_3d) + drm_property_blob_get(intel_state->hw.lut_3d); + return &intel_state->uapi; } @@ -167,6 +176,16 @@ intel_plane_destroy_state(struct drm_plane *plane, __drm_atomic_helper_plane_destroy_state(&plane_state->uapi); if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); + kfree(plane_state); } @@ -317,6 +336,14 @@ static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state) { if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); memset(&plane_state->hw, 0, sizeof(plane_state->hw)); } -- cgit v1.2.3 From 4db207599acfc9d676340daa2dc6b52bfca17db4 Mon Sep 17 00:00:00 2001 From: Kendall Willis Date: Wed, 6 May 2026 22:16:45 -0500 Subject: pmdomain: ti_sci: add wakeup constraint to parent devices of wakeup source Set wakeup constraint for any device in a wakeup path. All parent devices of a wakeup device should not be turned off during suspend. This ensures the wakeup device is kept on while the system is suspended. Cc: stable@vger.kernel.org Fixes: 9d8aa0dd3be4 ("pmdomain: ti_sci: add wakeup constraint management") Reported-by: Vitor Soares Closes: https://lore.kernel.org/linux-pm/c0fe43a2339c802e9ce5900092cd530a2ba17a6b.camel@gmail.com/ Signed-off-by: Kendall Willis Reviewed-by: Sebin Francis Signed-off-by: Ulf Hansson --- drivers/pmdomain/ti/ti_sci_pm_domains.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index 18d33bc35dee..949e4115f930 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -86,7 +86,7 @@ static inline void ti_sci_pd_set_wkup_constraint(struct device *dev) const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; int ret; - if (device_may_wakeup(dev)) { + if (device_may_wakeup(dev) || device_wakeup_path(dev)) { /* * If device can wakeup using IO daisy chain wakeups, * we do not want to set a constraint. -- cgit v1.2.3 From fba0510cd62666951dcc0221527edc0c47ae6599 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:36:27 +0200 Subject: pmdomain: imx: fix OF node refcount for_each_child_of_node_scoped() decrements the reference count of the nod after each iteration. Assigning it without incrementing the refcount to a dynamically allocated platform device will result in a double put in platform_device_release(). Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 3e4d109ee8fc ("pmdomain: imx: gpc: Simplify with scoped for each OF child loop") Signed-off-by: Bartosz Golaszewski Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index de695f1944ab..42e50c9b4fb9 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -487,7 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev) domain->ipg_rate_mhz = ipg_rate_mhz; pd_pdev->dev.parent = &pdev->dev; - pd_pdev->dev.of_node = np; + pd_pdev->dev.of_node = of_node_get(np); pd_pdev->dev.fwnode = of_fwnode_handle(np); ret = platform_device_add(pd_pdev); -- cgit v1.2.3 From ae7676952790f421c40918e2586a2c9f12a682b6 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Tue, 2 Jun 2026 14:50:14 -0300 Subject: drm/v3d: Fix vaddr leak when indirect CSD has zeroed workgroups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_rewrite_csd_job_wg_counts_from_indirect() maps both the indirect buffer and the workgroup buffer and is expected to release them before returning. When any of the workgroup counts read from the buffer is zero, the function bailed out early and skipped the cleanup, leaking the vaddr mappings of both BOs. Jump to the cleanup path instead of returning directly, so the mappings are always dropped. Cc: stable@vger.kernel.org Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Suggested-by: Jose Maria Casanova Crespo Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-1-654309e32bc0@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 94bf628dc91c..47f83936cd73 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -403,7 +403,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) - return; + goto unmap_bo; args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; @@ -428,6 +428,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) } } +unmap_bo: v3d_put_bo_vaddr(indirect); v3d_put_bo_vaddr(bo); } -- cgit v1.2.3 From 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Tue, 2 Jun 2026 14:50:15 -0300 Subject: drm/v3d: Skip CSD when it has zeroed workgroups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 registers. Kicking off a dispatch with a zero count in any of the three dimensions is invalid. First, the hardware will process 0 as 65536, while the user-space driver exposes a maximum of 65535. Over that, a submission with a zeroed workgroup dimension should be a no-op. These zeroed counts can reach the dispatch path through an indirect CSD job, whose workgroup counts are only known once the indirect buffer is read and may legitimately be zero, but such scenario should only result in a no-op. Overwrite the indirect CSD job workgroup counts with the indirect BO ones, even if they are zeroed, and don't submit the job to the hardware when any of the workgroup counts is zero, so the job completes immediately instead of running the shader. Cc: stable@vger.kernel.org Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") Suggested-by: Jose Maria Casanova Crespo Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_sched.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 47f83936cd73..8a635a9ec046 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -352,6 +352,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) return NULL; } + /* The HW interprets a workgroup size of 0 as 65536; however, the + * user-space driver exposes a maximum of 65535. Therefore, a 0 in + * any dimension means that we have no workgroups and the compute + * shader should not be dispatched. + */ + if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || + !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || + !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) + return NULL; + v3d->queue[V3D_CSD].active_job = &job->base; v3d_invalidate_caches(v3d); @@ -402,13 +412,13 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); - if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) - goto unmap_bo; - args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) + goto unmap_bo; + num_batches = DIV_ROUND_UP(indirect_csd->wg_size, 16) * (wg_counts[0] * wg_counts[1] * wg_counts[2]); -- cgit v1.2.3 From 15fe76e23615f502d051ef0768f86babaf08746c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 1 Jun 2026 13:52:31 -0300 Subject: RDMA/umem: Fix truncation for block sizes >= 4G When the iommu is used the linearization of the mapping can give a single block that is very large split across multiple SG entries. When __rdma_block_iter_next() reassembles the split SG entries it is overflowing the 32 bit stack values and computed the wrong DMA addresses for blocks after the truncation. Use the right types to hold DMA addresses. Link: https://patch.msgid.link/r/1-v1-88303e9e509f+f7-ib_umem_types_jgg@nvidia.com Cc: stable@vger.kernel.org Fixes: a808273a495c ("RDMA/verbs: Add a DMA iterator to return aligned contiguous memory blocks") Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/iter.c b/drivers/infiniband/core/iter.c index 8e543d100657..3ed351e8fcf6 100644 --- a/drivers/infiniband/core/iter.c +++ b/drivers/infiniband/core/iter.c @@ -19,8 +19,8 @@ EXPORT_SYMBOL(__rdma_block_iter_start); bool __rdma_block_iter_next(struct ib_block_iter *biter) { - unsigned int block_offset; - unsigned int delta; + dma_addr_t block_offset; + dma_addr_t delta; if (!biter->__sg_nents || !biter->__sg) return false; -- cgit v1.2.3 From 30252e6f71ba974ecf9cd8ce395b73b9900bc378 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 2 Jun 2026 13:24:01 +0200 Subject: drm/dumb-buffer: Drop buffer-size limits for now The size limits break some of the CI tests. So drop them for now. Keep the other overflow tests from commit 5ab62dd3687b ("drm: prevent integer overflows in dumb buffer creation helpers") in place. There is still a pre-existing overflow check for 32-bit type limits in drm_mode_create_dumb() that will catch the really absurd size requests. Drivers that still do not use drm_mode_size_dumb() should be updated. The helper calculates dumb-buffer geometry with overflow checks. Signed-off-by: Thomas Zimmermann Fixes: 5ab62dd3687b ("drm: prevent integer overflows in dumb buffer creation helpers") Reported-by: Jani Nikula Closes: https://lore.kernel.org/dri-devel/ddf0233e50044059c85279f928661563ef6a55bf@intel.com/ Cc: Rajat Gupta Cc: Thomas Zimmermann Cc: Maarten Lankhorst Cc: Maxime Ripard Acked-by: Jani Nikula Link: https://patch.msgid.link/20260602112842.252279-1-tzimmermann@suse.de --- drivers/gpu/drm/drm_dumb_buffers.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index cc99681a9ed0..2156dbe601c9 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -202,13 +202,6 @@ int drm_mode_create_dumb(struct drm_device *dev, if (!args->width || !args->height || !args->bpp) return -EINVAL; - /* Reject unreasonable inputs early. Dumb buffers are for software - * rendering; nothing legitimate needs more than 8192x8192 at 32bpp. - * This prevents overflows in downstream alignment helpers. - */ - if (args->width >= 8192 || args->height >= 8192 || args->bpp > 32) - return -EINVAL; - /* overflow checks for 32bit size calculations */ if (args->bpp > U32_MAX - 8) return -EINVAL; -- cgit v1.2.3 From 323c98a4ff06aa28114f2bf658fb43eb3b536bbc Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Mon, 25 May 2026 17:21:36 +0300 Subject: RDMA/core: Validate cpu_id against nr_cpu_ids in DMAH alloc The cpu_id attribute supplied by user space through UVERBS_ATTR_ALLOC_DMAH_CPU_ID is passed directly to cpumask_test_cpu() without first verifying that the value is within the valid CPU range. Passing such untrusted data to cpumask_test_cpu() may lead to an out-of-bounds read of the underlying cpumask bitmap: the helper expands to a test_bit() that indexes the bitmap by cpu_id / BITS_PER_LONG with no bound check. In addition, on kernels built with CONFIG_DEBUG_PER_CPU_MAPS it trips the WARN_ON_ONCE() in cpumask_check(); combined with panic_on_warn this turns a bad user input into a machine reboot. Reject any cpu_id that is not smaller than nr_cpu_ids with -EINVAL before it is used. Reported by Smatch. Fixes: d83edab562a4 ("RDMA/core: Introduce a DMAH object and its alloc/free APIs") Link: https://patch.msgid.link/r/20260525142136.28165-1-yishaih@nvidia.com Cc: stable@vger.kernel.org Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/ag68qoAW3P04J7pT@stanley.mountain/ Signed-off-by: Yishai Hadas Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_std_types_dmah.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c index 453ce656c6f2..97101e093826 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmah.c +++ b/drivers/infiniband/core/uverbs_std_types_dmah.c @@ -47,6 +47,11 @@ static int UVERBS_HANDLER(UVERBS_METHOD_DMAH_ALLOC)( if (ret) goto err; + if (dmah->cpu_id >= nr_cpu_ids) { + ret = -EINVAL; + goto err; + } + if (!cpumask_test_cpu(dmah->cpu_id, current->cpus_ptr)) { ret = -EPERM; goto err; -- cgit v1.2.3 From 6590fe323ce2807f5d9454e7fccf3fab875d4352 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 27 May 2026 23:58:54 -0700 Subject: drm/amd/display: add missing CSC entries for BT.2020 for DCE IPs DCE-based hardware does not have the CSC matrices for BT.2020, which causes the driver to fallback to the GPU built-in matrices. This does not appear to cause any issues for RGB sinks, but causes major color artifacts for YCbCr ones (e.g. black becomes green). This commit adds the missing CSC matrices (taken from DC common) to DCE CSC tables, resolving the issue. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/3358 Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5333 Assisted-by: oh-my-pi:GPT-5.5 Signed-off-by: Leorize Reviewed-by: Alex Hung Signed-off-by: Alex Deucher (cherry picked from commit 51e6668ab4baf55b082c376318d51ef965757196) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dce/dce_transform.c | 10 +++++++++- drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index c1448ae47366..0d312b40bcfa 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -110,7 +110,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index cf63fac82832..1ed018aaa4bb 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -88,7 +88,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { -- cgit v1.2.3 From e8b4d37eba05141ee01794fc6b7f2da808cee83b Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:44:15 -0400 Subject: drm/amd/display: Fix out-of-bounds read in dp_get_eq_aux_rd_interval() [Why & How] The aux_rd_interval array in struct dc_lttpr_caps is declared with MAX_REPEATER_CNT - 1 (7) elements, indexed 0..6. However, the offset parameter passed to dp_get_eq_aux_rd_interval() can be as large as MAX_REPEATER_CNT (8) when a sink reports 8 LTTPR repeaters via DPCD. This leads to an out-of-bounds read of aux_rd_interval[7] when offset is 8. Fix this by growing aux_rd_interval to MAX_REPEATER_CNT elements to accommodate the full range of valid repeater counts defined by the DP spec. Assisted-by: GitHub Copilot:Claude claude-4-opus Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit a55a458a8df37a65ffda5cf721d554a8f74f6b04) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dc_dp_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index 7fa336bf1115..7dd73eaaf940 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -1217,7 +1217,7 @@ struct dc_lttpr_caps { union dp_main_link_channel_coding_lttpr_cap main_link_channel_coding; union dp_128b_132b_supported_lttpr_link_rates supported_128b_132b_rates; union dp_alpm_lttpr_cap alpm; - uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; + uint8_t aux_rd_interval[MAX_REPEATER_CNT]; uint8_t lttpr_ieee_oui[3]; // Always read from closest LTTPR to host uint8_t lttpr_device_id[6]; // Always read from closest LTTPR to host }; -- cgit v1.2.3 From fb0707ce00eef4e2d60c3020e1c0432739703e4a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 4 May 2026 15:51:13 -0400 Subject: drm/amd/display: Clamp VBIOS HDMI retimer register count to array size [Why & How] The VBIOS integrated info tables (v1_11 and v2_1) contain HdmiRegNum and Hdmi6GRegNum fields that are used as loop bounds when copying retimer I2C register settings into fixed-size arrays (dp*_ext_hdmi_reg_settings[9] and dp*_ext_hdmi_6g_reg_settings[3]). These u8 fields are not validated before use, so a malformed VBIOS can specify values up to 255, causing an out-of-bounds heap write during driver probe. Clamp each register count to the destination array size using min_t() before the copy loops, in both get_integrated_info_v11() and get_integrated_info_v2_1(). Assisted-by: GitHub Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 5a7f0ef90195940c54b0f5bb85b87da55f038c69) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 48 ++++++++++++++-------- 1 file changed, 32 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index c51c4b2c6fae..e8d8947c552e 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -2600,14 +2600,16 @@ static enum bp_result get_integrated_info_v11( info_v11->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v11->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2616,14 +2618,16 @@ static enum bp_result get_integrated_info_v11( } info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v11->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2632,14 +2636,16 @@ static enum bp_result get_integrated_info_v11( } info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v11->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2648,14 +2654,16 @@ static enum bp_result get_integrated_info_v11( } info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v11->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2805,14 +2813,16 @@ static enum bp_result get_integrated_info_v2_1( info->ext_disp_conn_info.checksum = info_v2_1->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v2_1->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v2_1->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v2_1->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2820,14 +2830,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp1_ext_hdmi_slv_addr = info_v2_1->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v2_1->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v2_1->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2835,14 +2847,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp2_ext_hdmi_slv_addr = info_v2_1->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v2_1->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v2_1->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2850,14 +2864,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp3_ext_hdmi_slv_addr = info_v2_1->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v2_1->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v2_1->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; -- cgit v1.2.3 From adf67034b1f61f7119295208085bfd43f85f56af Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 11 May 2026 16:46:25 -0400 Subject: drm/amd/display: Fix NULL deref and buffer over-read in SDP debugfs [Why & How] dp_sdp_message_debugfs_write() dereferences connector->base.state->crtc without checking for NULL. A connector can be connected but not bound to any CRTC (e.g. after hot-plug before the next atomic commit), causing a kernel crash when writing to the sdp_message debugfs node. The function also ignores the user-provided size argument and always passes 36 bytes to copy_from_user(), reading past the user buffer when size < 36. Fix both issues by: - Returning -ENODEV when connector->base.state or state->crtc is NULL - Clamping write_size to min(size, sizeof(data)) Fixes: c7ba3653e977 ("drm/amd/display: Generic SDP message access in amdgpu") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 6ab4c36a522842ff70474a1c0af2e40e50fc8300) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 2409ac72b166..3a3d01ce0d42 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -1344,8 +1344,13 @@ static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *b if (size == 0) return 0; + if (!connector->base.state || !connector->base.state->crtc) + return -ENODEV; + acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state); + write_size = min_t(size_t, size, sizeof(data)); + r = copy_from_user(data, buf, write_size); write_size -= r; -- cgit v1.2.3 From da48bc4461b8a5ebfb9264c9b191a701d8e99009 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:52:15 -0400 Subject: drm/amd/display: Use krealloc_array() in dal_vector_reserve() [Why & How] dal_vector_reserve() computes the allocation size as "capacity * vector->struct_size" using uint32_t arithmetic, which can silently wrap to a small value on overflow. This would cause krealloc to return a smaller buffer than expected, leading to heap overflows on subsequent vector appends. Replace krealloc() with krealloc_array() which performs an internal overflow check and returns NULL on wrap, preventing the issue. Fixes: 2004f45ef83f ("drm/amd/display: Use kernel alloc/free") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 37668568641ccc4cc1dbca4923d0a16609dd5707) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/basics/vector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index e8736c134b8d..60bd9ead928a 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -289,8 +289,8 @@ bool dal_vector_reserve(struct vector *vector, uint32_t capacity) if (capacity <= vector->capacity) return true; - new_container = krealloc(vector->container, - capacity * vector->struct_size, GFP_KERNEL); + new_container = krealloc_array(vector->container, + capacity, vector->struct_size, GFP_KERNEL); if (new_container) { vector->container = new_container; -- cgit v1.2.3 From 49c3da65961fe9857c831d47fa1989084e87514a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 5 May 2026 11:50:07 -0400 Subject: drm/amd/display: Reject gpio_bitshift >= 32 in bios_parser_get_gpio_pin_info() [Why & How] gpio_bitshift is a uint8_t read directly from the VBIOS GPIO pin table. If the value is >= 32, the expression "1 << gpio_bitshift" triggers undefined behaviour in C (shift count exceeds type width). On x86 the shift is silently masked to 5 bits, producing an incorrect GPIO mask that may cause wrong MMIO register bits to be toggled. Validate gpio_bitshift before use and return BP_RESULT_BADBIOSTABLE for out-of-range values. Fixes: ae79c310b1a6 ("drm/amd/display: Add DCE12 bios parser support") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit eadf438ab8d370b9d19acee9359918c85afeb80d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index e8d8947c552e..4f213ea865b8 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -701,8 +701,10 @@ static enum bp_result bios_parser_get_gpio_pin_info( info->offset_en = info->offset + 1; info->offset_mask = info->offset - 1; - info->mask = (uint32_t) (1 << - header->gpio_pin[i].gpio_bitshift); + if (header->gpio_pin[i].gpio_bitshift >= 32) + return BP_RESULT_BADBIOSTABLE; + + info->mask = 1u << header->gpio_pin[i].gpio_bitshift; info->mask_y = info->mask + 2; info->mask_en = info->mask + 1; info->mask_mask = info->mask - 1; -- cgit v1.2.3 From f0f3981c43b32cadfe373d636d9e9ca522bb3702 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Thu, 7 May 2026 15:38:37 -0400 Subject: drm/amd/display: Clamp HDMI HDCP2 rx_id_list read to buffer size [Why & How] During HDCP 2.x repeater authentication over HDMI, the driver reads the sink's RxStatus register and extracts a 10-bit message size field (max value 1023). This value is used as the read length for the ReceiverID list without being clamped to the size of the destination buffer rx_id_list[177]. A malicious HDMI repeater could advertise a message size larger than the buffer, causing an out-of-bounds write during the I2C read. Clamp the read length in mod_hdcp_read_rx_id_list() to the size of the rx_id_list buffer, matching the approach already used in the DP branch. Fixes: eff682f83c9c ("drm/amd/display: Add DDC handles for HDCP2.2") Assisted-by: Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 229212219e4247d9486f8ba41ef087358490be09) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c index 0ca39873f807..324413a090bf 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c @@ -529,7 +529,8 @@ enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp) } else { status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, hdcp->auth.msg.hdcp2.rx_id_list, - hdcp->auth.msg.hdcp2.rx_id_list_size); + MIN(hdcp->auth.msg.hdcp2.rx_id_list_size, + sizeof(hdcp->auth.msg.hdcp2.rx_id_list))); } return status; } -- cgit v1.2.3 From ff287df16a1a58aca78b08d1f3ee09fc44da0351 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 12 May 2026 15:24:22 -0400 Subject: drm/amd/display: Bound VBIOS record-chain walk loops [Why & How] All record-chain walk loops in bios_parser.c and bios_parser2.c use for(;;) and only terminate on a 0xFF record_type sentinel or zero record_size. A malformed VBIOS image missing the terminator record causes unbounded iteration at probe time, potentially hundreds of thousands of iterations with record_size=1. In the final iterations near the BIOS image boundary, struct casts beyond the 2-byte header validated by GET_IMAGE can also read out of bounds. Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) iterations. The atombios.h defines up to 22 distinct record types and atomfirmware.h has 13. Assuming an average of less than 10 records per type (which is reasonable since most are connector- based) 256 is a generous upper bound. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Assisted-by: Copilot:claude-opus-4.6 Mythos Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/bios/bios_parser.c | 15 ++++++++---- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 27 ++++++++++++++-------- .../drm/amd/display/dc/bios/bios_parser_helper.h | 5 ++++ 3 files changed, 33 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index c307f42fe0b9..507b628abdb5 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -222,6 +222,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, ATOM_COMMON_RECORD_HEADER *header; ATOM_I2C_RECORD *record; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -234,7 +235,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -293,11 +294,12 @@ static enum bp_result bios_parser_get_device_tag_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -966,6 +968,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -975,7 +978,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -1670,6 +1673,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1679,7 +1683,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -2769,6 +2773,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, { (void)i; unsigned int j; + unsigned int n; struct bios_parser *bp; ATOM_BRACKET_LAYOUT_RECORD *record; ATOM_COMMON_RECORD_HEADER *record_header; @@ -2778,7 +2783,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, record = NULL; record_header = NULL; - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); if (record_header == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 4f213ea865b8..0e1f973326ed 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -396,6 +396,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, struct atom_i2c_record *record; struct atom_i2c_record dummy_record = {0}; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -429,7 +430,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, break; } - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -534,6 +535,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -542,7 +544,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -611,6 +613,7 @@ static struct atom_hpd_int_record *get_hpd_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -620,7 +623,7 @@ static struct atom_hpd_int_record *get_hpd_record( offset = le16_to_cpu(object->disp_recordoffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2195,6 +2198,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2203,7 +2207,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( offset = object->encoder_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2232,6 +2236,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2240,7 +2245,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2268,6 +2273,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2276,7 +2282,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2354,6 +2360,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2362,7 +2369,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -3263,6 +3270,7 @@ static enum bp_result update_slot_layout_info( { unsigned int record_offset; unsigned int j; + unsigned int n; struct atom_display_object_path_v2 *object; struct atom_bracket_layout_record *record; struct atom_common_record_header *record_header; @@ -3284,7 +3292,7 @@ static enum bp_result update_slot_layout_info( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, @@ -3378,6 +3386,7 @@ static enum bp_result update_slot_layout_info_v2( struct slot_layout_info *slot_layout_info) { unsigned int record_offset; + unsigned int n; struct atom_display_object_path_v3 *object; struct atom_bracket_layout_record_v2 *record; struct atom_common_record_header *record_header; @@ -3400,7 +3409,7 @@ static enum bp_result update_slot_layout_info_v2( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h index ab162f2fe577..19fd7aea18f1 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -37,4 +37,9 @@ void bios_set_scratch_critical_state(struct dc_bios *bios, bool state); #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) +/* Upper bound on the number of records in a VBIOS record chain. Prevents + * unbounded looping if the VBIOS image is malformed and lacks a terminator. + */ +#define BIOS_MAX_NUM_RECORD 256 + #endif -- cgit v1.2.3 From 181eda5549c5d9fad3fdb88b050fbf0844d884f8 Mon Sep 17 00:00:00 2001 From: Alysa Liu Date: Wed, 27 May 2026 11:31:35 -0400 Subject: drm/amdkfd: fix UAF race in destroy_queue_cpsch wait_on_destroy_queue() drops locks to wait for queue resume, allowing a concurrent destroy to free the queue. Use is_being_destroyed flag to serialize destruction. Reviewed-by: Amir Shetaia Signed-off-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ac081deaf16a639ea7dff2f285fe421a33c1ade0) --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 0d7296c739ed..0a408f95baac 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2502,6 +2502,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, if (pdd->qpd.is_debug) return ret; + if (q->properties.is_being_destroyed) + return -EBUSY; + q->properties.is_being_destroyed = true; if (pdd->process->debug_trap_enabled && q->properties.is_suspended) { @@ -2514,6 +2517,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, dqm_lock(dqm); } + if (ret) + q->properties.is_being_destroyed = false; + return ret; } @@ -2607,7 +2613,7 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm, return retval; failed_try_destroy_debugged_queue: - + q->properties.is_being_destroyed = false; dqm_unlock(dqm); return retval; } -- cgit v1.2.3 From 1a4a55f181a9584386701f9b4183d9ecc5271b21 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 27 May 2026 15:41:59 -0400 Subject: drm/amdgpu/sdma7.1: fix support for disable_kq Set the flag in the ring structure. Fixes: 80d4d3a45b86 ("drm/amdgpu/sdma7.1: add support for disable_kq") Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit e0a3aa8a6750e8cf067fe2146dc618ffd296d5ef) --- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 061934a2e93a..9c9bbe043a47 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1316,6 +1316,7 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; + ring->no_user_submission = adev->sdma.no_user_submission; for (xcc_id = 0; xcc_id < fls(adev->gfx.xcc_mask); xcc_id++) { if (adev->sdma.instance[i].xcc_id == GET_INST(GC, xcc_id)) -- cgit v1.2.3 From e0153b94a1d104cf4545878c83302672573de65e Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Thu, 14 May 2026 17:21:09 +0800 Subject: drm/amdgpu: unmap userq for evicting user queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the driver only preempts queues, there can still be inflight waves, pending dispatch state, or resume/redispatch possibility tied to the same queue. Then the VM/TTM side may proceed to move/unmap queue related BOs during evicting userq objects while shader TCP clients still need to access them. So for eviction, unmap is safer because it makes the queue nonrunnable before memory backing is invalidated. Meanwhile, for a idle queue it's more sutiable for unmapping it rather preempt and unmapping also can save more processing time than preempt. Signed-off-by: Prike Liang Reviewed-by: Christian König Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit d87c9d86727a0bcc95c3009a213a1b27a11b691e) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index cf192500800f..e937099de3e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -887,7 +887,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) continue; } - r = amdgpu_userq_restore_helper(queue); + r = amdgpu_userq_map_helper(queue); if (r) ret = r; @@ -1124,7 +1124,7 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) /* Try to unmap all the queues in this process ctx */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { - r = amdgpu_userq_preempt_helper(queue); + r = amdgpu_userq_unmap_helper(queue); if (r) ret = r; } -- cgit v1.2.3 From a40412285ec17a69ed728675a56b7ad479c86e36 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Mon, 25 May 2026 13:22:04 +0200 Subject: drm/amdgpu: Align amdgpu_gtt_mgr entries to TLB size on all SI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems that Pitcairn has the same issues as Tahiti with regards to the TLB size. This commit fixes a VCE1 FW validation timeout on suspend/resume on Pitcairn. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5336 Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 629279e2e798cd161cf74f40aaebfeb16d45eb01) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index a5d26b943f6d..d23a91d029aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -203,7 +203,7 @@ int amdgpu_gtt_mgr_alloc_entries(struct amdgpu_gtt_mgr *mgr, int r; /* Align to TLB L2 cache entry size to work around "V bit HW bug" */ - if (adev->asic_type == CHIP_TAHITI) { + if (adev->family == AMDGPU_FAMILY_SI) { alignment = 32 * 1024 / AMDGPU_GPU_PAGE_SIZE; num_pages = ALIGN(num_pages, alignment); } -- cgit v1.2.3 From 2493d87bb4c31ec9ca7f0ef7257e33b8b175f913 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 21 May 2026 22:36:37 +0800 Subject: drm/amd/pm: apply SMU 13.0.10 workaround during MP1 unload On SMU v13.0.10, sending PrepareMp1ForUnload with the default parameter may leave the device in an inaccessible state. This can affect runtime power management and partial PnP flows. e.g: kexec, driver unload, boco/d3cold. Pass the required workaround parameter 0x55, when preparing MP1 for unload on SMU v13.0.10, keep the existing behavior for other SMU versions. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5133 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 4e8ee1afeedb8d24dd22cdd5ae9f98a6d76ebe4b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 0a7f5fa3c1d3..fa861ec4d700 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2801,11 +2801,19 @@ static void smu_v13_0_0_i2c_control_fini(struct smu_context *smu) static int smu_v13_0_0_set_mp1_state(struct smu_context *smu, enum pp_mp1_state mp1_state) { + uint32_t param; int ret; switch (mp1_state) { case PP_MP1_STATE_UNLOAD: - ret = smu_cmn_set_mp1_state(smu, mp1_state); + /* + * NOTE: Param 0x55 comes from PMFW 80.31.0, ignored in older versions. + * No PMFW version check required. + */ + param = amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 10) ? + 0x55 : 0x00; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PrepareMp1ForUnload, + param, NULL); break; default: /* Ignore others */ -- cgit v1.2.3 From bb204f19e4a115f094a6a3c4d82fcf48862d0766 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 19 May 2026 11:18:12 +0800 Subject: drm/amd/pm: fix smu13 power limit default/cap calculation smu_v13_0_0_get_power_limit() and smu_v13_0_7_get_power_limit() mix runtime power_limit with PP table limits when reporting default/min/max. When current power limit query succeeds, default_power_limit was set to the runtime value instead of the PP table default, and min/max could be derived from inconsistent bases (MsgLimits/runtime), leading to incorrect cap info. Use SocketPowerLimitAc/Dc as the PP default base (pp_limit), keep current_power_limit as runtime value, and derive min/max from pp_limit with OD percentages. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5227 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit 1eaf26db95901ca70737503a89b831dd763c8453) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 32 ++++++++++++---------- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 32 ++++++++++++---------- 2 files changed, 35 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index fa861ec4d700..7f8d4bb47d02 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2390,28 +2390,30 @@ static int smu_v13_0_0_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_0_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_0_powerplay_table *powerplay_table = (struct smu_13_0_0_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + *current_power_limit = pp_limit; + } - if (current_power_limit) - *current_power_limit = power_limit; if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2425,15 +2427,15 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 5abf2b0703c6..0f774b0920ce 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2372,28 +2372,32 @@ static int smu_v13_0_7_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_7_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_7_powerplay_table *powerplay_table = (struct smu_13_0_7_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + power_limit = pp_limit; - if (current_power_limit) *current_power_limit = power_limit; + } + if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2407,15 +2411,15 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } -- cgit v1.2.3 From ee193c5bbd5e2b56bbeb54ef554414b43a6fc896 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 29 May 2026 11:47:31 +0800 Subject: drm/amd/pm: mark metrics.energy_accumulator is invalid for smu 14.0.2 EnergyAccumulator is unsupported on SMU 14.0.2, mark it invalid. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 646b05043eeed04b51c14aad22a400a8250af4b7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') 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 5ce4e982ca33..fdc1456b885c 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 @@ -2152,7 +2152,6 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, metrics->Vcn1ActivityPercentage); gpu_metrics->average_socket_power = metrics->AverageSocketPower; - gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; -- cgit v1.2.3 From a169b326bab4a3617a9b1d34beddaaa6f798aa20 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 11 May 2026 16:33:37 +0800 Subject: drm/amd/pm: zero unused SMU argument registers SMU messages may use fewer arguments than the available argument registers, the previous code only wrote used registers and left the rest unchanged, so stale values from a prior message could persist. Write all argument registers for each message and zero the unused tail to keep command arguments deterministic and avoid unintended carry-over. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit e03b635f61f77ebd5107ef82f48e3221cb695856) --- drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 90c7127beabf..fe97fda8bfe9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -272,11 +272,15 @@ static void __smu_msg_v1_send(struct smu_msg_ctl *ctl, u16 index, { struct amdgpu_device *adev = ctl->smu->adev; struct smu_msg_config *cfg = &ctl->config; + u32 arg; int i; WREG32(cfg->resp_reg, 0); - for (i = 0; i < args->num_args; i++) - WREG32(cfg->arg_regs[i], args->args[i]); + for (i = 0; i < cfg->num_arg_regs; i++) { + /* NOTE: Clear unused argument registers to avoid stale values. */ + arg = i < args->num_args ? args->args[i] : 0; + WREG32(cfg->arg_regs[i], arg); + } WREG32(cfg->msg_reg, index); } -- cgit v1.2.3 From ae4e30f24d67075dc975002effa68d424c7ff7e3 Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Tue, 28 Apr 2026 17:45:06 -0400 Subject: drm/amdgpu: Use asic specific pte_addr_mask For PTE creation use asic specific physical page base address mask v2: Change variable name from pa_mask to pte_addr_mask Signed-off-by: Harish Kasiviswanathan Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit 2ea989885941a6e5607ef86dbe309e90b7191f21) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 4 ++++ drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 1 + 9 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 3d9497d121ca..13a5acdf8da3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -170,7 +170,7 @@ int amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev, void *cpu_pt_addr, /* * The following is for PTE only. GART does not have PDEs. */ - value = addr & 0x0000FFFFFFFFF000ULL; + value = addr & adev->gmc.pte_addr_mask; value |= flags; writeq(value, ptr + (gpu_page_idx * 8)); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index 6ab4c1e297fc..d03536b969b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -280,6 +280,7 @@ struct amdgpu_gmc { u64 real_vram_size; int vram_mtrr; u64 mc_mask; + uint64_t pte_addr_mask; const struct firmware *fw; /* MC firmware */ uint32_t fw_version; struct amdgpu_irq_src vm_fault; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index e1ace7d44ffd..f5bdfea54afa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -847,6 +847,7 @@ static int gmc_v10_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 94d6631ce0bc..807bd180b9d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -821,6 +821,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index e10ac9788d13..52c161c2df0a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -814,6 +814,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) { int r, vram_width = 0, vram_type = 0, vram_vendor = 0; struct amdgpu_device *adev = ip_block->adev; + uint64_t pte_addr_mask = 0; int i; adev->mmhub.funcs->init(adev); @@ -843,6 +844,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 256 * 1024, 9, 3, 48); + pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ break; case IP_VERSION(12, 1, 0): bitmap_set(adev->vmhubs_mask, AMDGPU_GFXHUB(0), @@ -855,6 +857,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 128 * 1024 * 1024, 9, 4, 57); + pte_addr_mask = 0x000FFFFFFFFFF000ULL; /* 52 bit PA */ break; default: break; @@ -911,6 +914,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = AMDGPU_GMC_HOLE_MASK; + adev->gmc.pte_addr_mask = pte_addr_mask; r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index cc272a96fcef..6aa581b1c148 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -836,6 +836,7 @@ static int gmc_v6_0_sw_init(struct amdgpu_ip_block *ip_block) amdgpu_vm_adjust_size(adev, 64, 9, 1, 40); adev->gmc.mc_mask = 0xffffffffffULL; + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index bb16ba2ef6fd..2b0362c4d9eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1016,6 +1016,7 @@ static int gmc_v7_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index a59174f6bcc1..fbccfcb3d7cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1131,6 +1131,7 @@ static int gmc_v8_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index e7b78027002b..c6dbe25f2bd9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1994,6 +1994,7 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ dma_addr_bits = amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 4, 2) ? -- cgit v1.2.3 From e3fa02872e223d24a925eb876b31c00d833bce7c Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Tue, 12 May 2026 10:57:49 -0400 Subject: drm/amdgpu: drm/amdgpu: Set correct DMA mask for gfx12.1 Set correct DMA mask for gfx12 Signed-off-by: Harish Kasiviswanathan Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit a2ef14ee2593b48242b8d90f229f71c1710529da) --- drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index 52c161c2df0a..8dc9c053897b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -812,7 +812,7 @@ static int gmc_v12_0_gart_init(struct amdgpu_device *adev) static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) { - int r, vram_width = 0, vram_type = 0, vram_vendor = 0; + int r, vram_width = 0, vram_type = 0, vram_vendor = 0, dma_addr_bits; struct amdgpu_device *adev = ip_block->adev; uint64_t pte_addr_mask = 0; int i; @@ -845,6 +845,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) */ amdgpu_vm_adjust_size(adev, 256 * 1024, 9, 3, 48); pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ + dma_addr_bits = 44; break; case IP_VERSION(12, 1, 0): bitmap_set(adev->vmhubs_mask, AMDGPU_GFXHUB(0), @@ -858,9 +859,12 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) */ amdgpu_vm_adjust_size(adev, 128 * 1024 * 1024, 9, 4, 57); pte_addr_mask = 0x000FFFFFFFFFF000ULL; /* 52 bit PA */ + dma_addr_bits = 52; break; default: - break; + dev_warn(adev->dev, "Unrecognized GC IP version: 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } /* This interrupt is VMC page fault.*/ @@ -916,13 +920,13 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gmc.mc_mask = AMDGPU_GMC_HOLE_MASK; adev->gmc.pte_addr_mask = pte_addr_mask; - r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); + r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(dma_addr_bits)); if (r) { drm_warn(adev_to_drm(adev), "No suitable DMA available.\n"); return r; } - adev->need_swiotlb = drm_need_swiotlb(44); + adev->need_swiotlb = drm_need_swiotlb(dma_addr_bits); r = gmc_v12_0_mc_init(adev); if (r) -- cgit v1.2.3 From 58bafc666c484b21839a2d27e923ae1b2727a1df Mon Sep 17 00:00:00 2001 From: Christian König Date: Wed, 18 Feb 2026 13:05:46 +0100 Subject: drm/amdgpu: fix waiting for all submissions for userptrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wait for all submissions when userptrs need to be invalidated by the MMU notifier, not just the one the userptr was involved into. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 91250893cbaa25c86872deca95a540d08de1f91e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 5bfa5a84b09c..e452444b33b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -67,6 +67,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo; long r; if (!mmu_notifier_range_blockable(range)) @@ -77,8 +78,9 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); amdgpu_vm_bo_invalidate(bo, false); - r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, - false, MAX_SCHEDULE_TIMEOUT); + r = dma_resv_wait_timeout(vm_root->tbo.base.resv, + DMA_RESV_USAGE_BOOKKEEP, false, + MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); if (r <= 0) DRM_ERROR("(%ld) failed to wait for user bo\n", r); -- cgit v1.2.3 From 40bab7c606702e8ea3e95c6f30c99cc8295826af Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Mon, 25 May 2026 13:45:02 +0200 Subject: drm/amdgpu/gfxhub: Program CRASH_ON_*_FAULT bits to 0 as needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the fault stop mode isn't AMDGPU_VM_FAULT_STOP_ALWAYS, these bits should be programmed to 0. Program CRASH_ON_NO_RETRY_FAULT and CRASH_ON_RETRY_FAULT always, to make sure to clear the bits when we don't want to crash. Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d0cd99e73090700b7a942b98a3327ec966597d0a) --- drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c | 14 ++++++-------- drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c | 10 ++++------ 9 files changed, 38 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c index f9949fedfbb9..f2fe6f5bc7f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c @@ -449,12 +449,10 @@ static void gfxhub_v11_5_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c index 7609b9cecae8..efcaca70c27a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c @@ -454,12 +454,10 @@ static void gfxhub_v12_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c index 3544eb42dca6..4c2fd1e6616e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c @@ -633,19 +633,17 @@ static void gfxhub_v12_1_xcc_set_fault_enable_default(struct amdgpu_device *adev tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL_LO32, OTHER_CLIENT_ID_NO_RETRY_FAULT_INTERRUPT, value); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_LO32, - CRASH_ON_NO_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_LO32, + CRASH_ON_NO_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_LO32, tmp); tmp = RREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_HI32, - CRASH_ON_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_HI32, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c index a7bfc9f41d0e..bfe247b1a333 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c @@ -403,12 +403,10 @@ static void gfxhub_v1_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c index 6c03bf9f1ae8..fbdf46070b38 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c @@ -516,12 +516,10 @@ static void gfxhub_v1_2_xcc_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regVM_L2_PROTECTION_FAULT_CNTL, tmp); } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c index 793faf62cb07..9ea593e2c719 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c @@ -418,12 +418,10 @@ static void gfxhub_v2_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c index aceb8447feac..30b90d35abd0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c @@ -449,12 +449,10 @@ static void gfxhub_v2_1_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c index abe30c8bd2ba..f089f70571aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c @@ -446,12 +446,10 @@ static void gfxhub_v3_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c index b3ef6e71811f..128115a2cb45 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c @@ -434,12 +434,10 @@ static void gfxhub_v3_0_3_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } -- cgit v1.2.3 From e47b0056a08dc70430ffc44bbf62197e7d1ff8ea Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Fri, 29 May 2026 13:50:38 -0400 Subject: drm/amdgpu: set noretry=1 as default for GFX 10.1.x (Navi10/12/14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: While developing the amd_close_race IGT test (which intentionally triggers execute permission faults by removing VM_PAGE_EXECUTABLE from GPU page table entries), we discovered that on Navi10 (GFX 10.1.x) these faults produce zero diagnostic output. The GPU simply hangs silently for ~10s until the scheduler timeout fires. There is no way to distinguish an execute permission fault from any other type of GPU hang. Root cause: GFX 10.1.x defaults to noretry=0, which sets RETRY_PERMISSION_OR_INVALID_PAGE_FAULT=1 in the GFXHUB UTCL2 registers (gfxhub_v2_0.c line 313). With this bit set, permission faults (valid PTE, wrong R/W/X bits) are handled entirely within the UTCL1/UTCL2 hardware loop: UTCL2 returns an XNACK to UTCL1, and UTCL1 re-requests the translation indefinitely, expecting software to eventually fix the permission bits (as happens in SVM/HMM recovery). No interrupt of any kind reaches the IH ring. This is different from invalid-page faults (V=0) which DO generate a retry fault interrupt that the driver can escalate to a no-retry fault. Permission faults with valid PTEs loop silently forever in hardware. GFX 10.3+ already defaults to noretry=1, which makes permission faults generate immediate L2 protection fault interrupts. GFX 10.1.x was inadvertently left out of this default. Fix: Change the noretry=1 threshold from IP_VERSION(10, 3, 0) to IP_VERSION(10, 1, 0) in amdgpu_gmc_noretry_set(). This is a one-line change that aligns GFX 10.1.x behavior with GFX 10.3+ and all newer generations. With noretry=1, the existing non-retry fault handler (gmc_v10_0_process_interrupt) already decodes and prints the full GCVM_L2_PROTECTION_FAULT_STATUS register including PERMISSION_FAULTS, faulting address, VMID, PASID, and process name. No additional logging code is needed — the fix is purely routing permission faults to the existing, fully-capable non-retry interrupt handler. v2: Dropped GFX10-specific logging from gmc_v10_0.c and kfd_int_process_v10.c (Felix Kuehling). v1 added logging in the retry fault handler, but with noretry=1 permission faults take the non-retry path — the v1 retry handler code was dead and would never execute. Tested on Navi10 (GFX 10.1.10): - Execute permission faults now produce immediate, clear output: [gfxhub] page fault (src_id:0 ring:64 vmid:4 pasid:592) Process amd_close_race pid 13380 thread amd_close_race pid 13384 in page at address 0x40001000 from client 0x1b (UTCL2) GCVM_L2_PROTECTION_FAULT_STATUS:0x00700881 PERMISSION_FAULTS: 0x8 - No regressions with properly-mapped GPU workloads Cc: Christian Koenig Cc: Alex Deucher Cc: Felix Kuehling Signed-off-by: Vitaly Prosyak Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit eb21edd24c40d81066753f8ac6f23bce15745395) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 13a5acdf8da3..c076c5f06e77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -1003,7 +1003,7 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) gc_ver == IP_VERSION(9, 4, 3) || gc_ver == IP_VERSION(9, 4, 4) || gc_ver == IP_VERSION(9, 5, 0) || - gc_ver >= IP_VERSION(10, 3, 0)); + gc_ver >= IP_VERSION(10, 1, 0)); if (!amdgpu_sriov_xnack_support(adev)) gmc->noretry = 1; -- cgit v1.2.3 From 2bd550b547deabef98bd3b017ff743b7c34d3a6d Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 16:56:46 +0000 Subject: drm/amdkfd: fix NULL dereference in get_queue_ids() When usr_queue_id_array is NULL and num_queues is non-zero, get_queue_ids() returns NULL. The callers check only IS_ERR() on the return value; since IS_ERR(NULL) == false the check passes, and suspend_queues() calls q_array_invalidate() which immediately dereferences NULL while iterating num_queues times. Userspace can trigger this via kfd_ioctl_set_debug_trap() by supplying num_queues > 0 with a zero queue_array_ptr, causing a kernel panic. A NULL usr_queue_id_array with num_queues == 0 is a legitimate no-op (q_array_invalidate never executes, and resume_queues already guards all queue_ids dereferences behind a NULL check). Return ERR_PTR(-EINVAL) only when num_queues is non-zero and the pointer is absent; both callers already propagate IS_ERR() returns correctly to userspace. Fixes: a70a93fa568b ("drm/amdkfd: add debug suspend and resume process queues operation") Signed-off-by: Muhammad Bilal Signed-off-by: Alex Deucher (cherry picked from commit f165a82cdf503884bb1797771c61b2fcc72113d4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 0a408f95baac..31187ddbb79e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3315,7 +3315,7 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { if (!usr_queue_id_array) - return NULL; + return num_queues ? ERR_PTR(-EINVAL) : NULL; if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) return ERR_PTR(-EINVAL); -- cgit v1.2.3 From 352ea59028ea48a6fff77f19ae28f98f71946a80 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 28 May 2026 12:54:39 -0400 Subject: drm/amdkfd: Fix buffer overflow in SDMA queue checkpoint/restore on GFX11 The v11 MQD manager incorrectly assigned the CP-compute variants of checkpoint_mqd/restore_mqd for KFD_MQD_TYPE_SDMA queues. These functions use sizeof(struct v11_compute_mqd) (2048 bytes) instead of sizeof(struct v11_sdma_mqd) (512 bytes), causing a 1536-byte overflow. During CRIU checkpoint of an SDMA queue on Navi3x: - checkpoint_mqd() reads 2048 bytes from a 512-byte SDMA MQD buffer, leaking 1536 bytes of adjacent GTT memory to userspace During CRIU restore: - restore_mqd() writes 2048 bytes into a 512-byte SDMA MQD buffer, corrupting 1536 bytes of adjacent GTT memory (often the ring buffer or neighboring MQDs) This is a copy-paste regression unique to v11. All other ASIC backends (cik, vi, v9, v10, v12) correctly use the SDMA-specific variants. Add checkpoint_mqd_sdma() and restore_mqd_sdma() functions that properly handle the smaller v11_sdma_mqd structure, matching the pattern used in other MQD managers. Fixes: cc009e613de6 ("drm/amdkfd: Add KFD support for soc21 v3") Assisted-by: Claude:Sonnet 4-5 Signed-off-by: Andrew Martin Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 6fa41db7ffdec97d62433adf03b7b9b759af8c2c) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index a1e3cf2384dd..527c531676e4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -320,8 +320,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi static void restore_mqd(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, - struct queue_properties *qp, - const void *mqd_src, + struct queue_properties *qp, const void *mqd_src, const void *ctl_stack_src, const u32 ctl_stack_size) { uint64_t addr; @@ -337,14 +336,48 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; m->cp_hqd_pq_doorbell_control = - qp->doorbell_off << - CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; - pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", - m->cp_hqd_pq_doorbell_control); + qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control); qp->is_active = 0; } +static void checkpoint_mqd_sdma(struct mqd_manager *mm, + void *mqd, + void *mqd_dst, + void *ctl_stack_dst) +{ + struct v11_sdma_mqd *m; + + m = get_sdma_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_sdma_mqd)); +} + +static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, + const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_sdma_mqd *m; + + m = (struct v11_sdma_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + m->sdmax_rlcx_doorbell_offset = + qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, @@ -529,8 +562,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; - mqd->checkpoint_mqd = checkpoint_mqd; - mqd->restore_mqd = restore_mqd; + mqd->checkpoint_mqd = checkpoint_mqd_sdma; + mqd->restore_mqd = restore_mqd_sdma; mqd->mqd_size = sizeof(struct v11_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) -- cgit v1.2.3 From 2a07f3fa4998e2ef248e1f55cd3776348758aec0 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 25 May 2026 13:18:00 +0530 Subject: drm/amdgpu/userq: remove the vital queue unmap logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mesa userqueues free does not wait for the free to complete and go ahead in unmapping the vital bos while kernel is still in queue free and corresponding cleanup. So ideally we don't need the logging for that and hence remove the warn message as this is expected behaviour and functionally, we are making sure to wait for the required fences before unmap. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 758a868043dcb07eca923bc451c16da3e73dc47c) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 3 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index e937099de3e7..986ef5ca0087 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1344,8 +1344,7 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev, } void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr) + struct amdgpu_bo_va_mapping *mapping) { u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_bo_va *bo_va = mapping->bo_va; @@ -1354,12 +1353,9 @@ void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, if (!ip_mask) return; - dev_warn_once(adev->dev, "now unmapping a vital queue va:%llx\n", saddr); /** - * The userq VA mapping reservation should include the eviction fence, - * if the eviction fence can't signal successfully during unmapping, - * then driver will warn to flag this improper unmap of the userq VA. - * Note: The eviction fence may be attached to different BOs, and this + * The userq VA mapping reservation should include the eviction fence. + * Note: The eviction fence may be attached to different BOs and this * unmap is only for one kind of userq VAs, so at this point suppose * the eviction fence is always unsignaled. */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 28cfc6682333..d1751febaefe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -182,6 +182,5 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, u64 addr, u64 expected_size, u64 *va_out); void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr); + struct amdgpu_bo_va_mapping *mapping); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index c9f88ecce1a7..381901bc539f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2006,7 +2006,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, * from user space. */ if (unlikely(bo_va->userq_va_mapped)) - amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr); + amdgpu_userq_gem_va_unmap_validate(adev, mapping); list_del(&mapping->list); amdgpu_vm_it_remove(mapping, &vm->va); -- cgit v1.2.3 From 5af28a22ce834544bb22efbba30ffb837098b2b8 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Tue, 26 May 2026 10:25:26 +0800 Subject: drm/amdgpu: improve the userq seq BO free bit lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use find_next_zero_bit() to locate the next free seq slot bit instead of the current walk, for more efficient bitmap scanning. Signed-off-by: Prike Liang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ff905a9b6228de9eedd0db71ecb1bdde91fb898d) --- drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c index f4be19223588..21a225b0116a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c @@ -173,16 +173,17 @@ error: int amdgpu_seq64_alloc(struct amdgpu_device *adev, u64 *va, u64 *gpu_addr, u64 **cpu_addr) { - unsigned long bit_pos; + unsigned long bit_pos = 0; - for (;;) { - bit_pos = find_first_zero_bit(adev->seq64.used, adev->seq64.num_sem); + do { + bit_pos = find_next_zero_bit(adev->seq64.used, + adev->seq64.num_sem, bit_pos); if (bit_pos >= adev->seq64.num_sem) return -ENOSPC; - if (!test_and_set_bit(bit_pos, adev->seq64.used)) break; - } + bit_pos++; + } while (1); *va = bit_pos * sizeof(u64) + amdgpu_seq64_get_va_base(adev); -- cgit v1.2.3 From 14ad7e1e6e2cd44f866e2dbb3f6a2b2f4a39b96d Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 25 May 2026 09:56:23 +0530 Subject: drm/amdgpu/userq: move wptr_obj cleanup in mqd_destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case when queue_create fails and mqd has already been allocated and hence wptr_obj is not cleaned up. So moving that cleanup part to mqd_destroy so it takes care of all the cases of clean up and during tear down of the queue. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 43355f62cd2ef5386c2693df537c232ea0f2ce6c) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 4 ---- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 986ef5ca0087..59ffaa7b61c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -532,10 +532,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); - amdgpu_bo_unref(&queue->wptr_obj.obj); kfree(queue); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 98aa00eeb2f4..4cbd46f53e85 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -467,6 +467,11 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) kfree(queue->userq_prop); amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, &queue->mqd.cpu_ptr); + + amdgpu_bo_reserve(queue->wptr_obj.obj, true); + amdgpu_bo_unpin(queue->wptr_obj.obj); + amdgpu_bo_unreserve(queue->wptr_obj.obj); + amdgpu_bo_unref(&queue->wptr_obj.obj); } static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) -- cgit v1.2.3 From ec4c462e2d8161b32038e21e7187f4a15fe1661d Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Wed, 27 May 2026 18:49:31 +0530 Subject: drm/amdgpu: Fix incorrect VRAM GART mappings on non-4K page size systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When mapping VRAM pages into the GART page table, amdgpu_gart_map_vram_range() assumes that the system page size is the same as the GPU page size. On systems with non-4K page sizes, multiple GPU pages can exist within a single CPU page. As a result, the mappings are created incorrectly because fewer page table entries are programmed than required. Fix this by programming the mappings correctly for non-4K page size systems. Fixes: 237d623ae659 ("drm/amdgpu/gart: Add helper to bind VRAM pages (v2)") Reviewed-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit a8f0bc22388f74e0cf4ed8b7d1846c580eaf44cc) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index b6f849d51c2e..c4c21dbbbdbf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -394,7 +394,8 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, uint64_t start_page, uint64_t num_pages, uint64_t flags, void *dst) { - u32 i, idx; + u32 i, j, t, idx; + u64 page_base; /* The SYSTEM flag indicates the pages aren't in VRAM. */ WARN_ON_ONCE(flags & AMDGPU_PTE_SYSTEM); @@ -402,9 +403,12 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, if (!drm_dev_enter(adev_to_drm(adev), &idx)) return; - for (i = 0; i < num_pages; ++i) { - amdgpu_gmc_set_pte_pde(adev, dst, - start_page + i, pa + AMDGPU_GPU_PAGE_SIZE * i, flags); + page_base = pa; + for (i = 0, t = 0; i < num_pages; i++) { + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + amdgpu_gmc_set_pte_pde(adev, dst, start_page + t, page_base, flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } } drm_dev_exit(idx); -- cgit v1.2.3 From 03b70e0d8aa26bab89a0f1394c1c80a871925e42 Mon Sep 17 00:00:00 2001 From: Priya Hosur Date: Thu, 7 May 2026 13:31:37 +0530 Subject: drm/amd/pm: smu_v14_0_0: use SoftMin for gfxclk in set_soft_freq_limited_range In smu_v14_0_0_set_soft_freq_limited_range(), the gfxclk floor is programmed via SetHardMinGfxClk together with SetSoftMaxGfxClk. Under power_dpm_force_performance_level=high this pins HardMin to peak gfxclk. In PMFW arbitration HardMin has higher priority than SoftMax, so the firmware thermal/PPT throttler cannot clamp gfxclk via SoftMax once HardMin is set to peak. Replace SetHardMinGfxClk with SetSoftMinGfxclk so the driver still requests peak performance but the firmware throttler retains the ability to clamp gfxclk under thermal/PPT pressure. SoftMax handling is unchanged and no other clock domains are affected. Signed-off-by: Priya Hosur Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 3ea273267fd29cbf6d83ee72329f59eb5042605b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index a28624d4847a..75719c47a41e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -1231,7 +1231,8 @@ static int smu_v14_0_0_set_soft_freq_limited_range(struct smu_context *smu, switch (clk_type) { case SMU_GFXCLK: case SMU_SCLK: - msg_set_min = SMU_MSG_SetHardMinGfxClk; + /* SoftMin lets PMFW throttle gfxclk; HardMin would override SoftMax. */ + msg_set_min = SMU_MSG_SetSoftMinGfxclk; msg_set_max = SMU_MSG_SetSoftMaxGfxClk; break; case SMU_FCLK: -- cgit v1.2.3 From 732fd9f0b9c1cdc6dfd77162ded60df005182cc0 Mon Sep 17 00:00:00 2001 From: Cunlong Li Date: Thu, 28 May 2026 10:48:44 +0800 Subject: zram: fix use-after-free in zram_bvec_write_partial() zram_read_page() picks the sync or async backing device read path based on whether the parent bio is NULL. zram_bvec_write_partial() passes its parent bio down, so for ZRAM_WB slots the read is dispatched asynchronously and zram_read_page() returns 0 while the bio is still in flight. The caller then runs memcpy_from_bvec(), zram_write_page() and __free_page() on the buffer, leaving the async read to write into a freed page. zram_bvec_read_partial() was switched to NULL in commit 4e3c87b9421d ("zram: fix synchronous reads") for the same reason; the write_partial counterpart was missed. Link: https://lore.kernel.org/20260528-zram-v3-1-cab86eef8764@gmail.com Fixes: 8e654f8fbff5 ("zram: read page from backing device") Reviewed-by: Christoph Hellwig Reviewed-by: Sergey Senozhatsky Signed-off-by: Cunlong Li Cc: Jens Axboe Cc: Minchan Kim Cc: Yisheng Xie Cc: Signed-off-by: Andrew Morton --- drivers/block/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 07111455eecf..e11ee1ed3832 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2337,7 +2337,7 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, if (!page) return -ENOMEM; - ret = zram_read_page(zram, page, index, bio); + ret = zram_read_page(zram, page, index, NULL); if (!ret) { memcpy_from_bvec(page_address(page) + offset, bvec); ret = zram_write_page(zram, page, index); -- cgit v1.2.3 From fa7c84726dc217ce0c183926ef9411636c7a2213 Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:15 +0530 Subject: Revert "drm/xe: Skip exec queue schedule toggle if queue is idle during suspend" This reverts commit 8533051ce92015e9cc6f75e0d52119b9d91610b6. The idle-skip optimization bypasses GuC suspend, so the GPU may not perform the context switch that flushes TLB entries for invalidated userptr VMAs. In LR/preempt-fence VM mode, this can lead to missed TLB invalidation and page faults during userptr invalidation tests. Restore unconditional schedule toggling on suspend so the context-switch TLB flush is always performed. This optimization will be reintroduced with a fix that does not skip suspend in LR/preempt-fence VM mode. Fixes: 8533051ce920 ("drm/xe: Skip exec queue schedule toggle if queue is idle during suspend") Cc: stable@vger.kernel.org # v7.0+ Suggested-by: Thomas Hellstrom Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-2-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 6a1e7934d9a6cf46aecae00a99c2603d1295e170) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_exec_queue.h | 17 ---------- drivers/gpu/drm/xe/xe_guc_submit.c | 55 ++------------------------------- drivers/gpu/drm/xe/xe_hw_engine_group.c | 10 ++---- 3 files changed, 5 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index a82d99bd77bc..0225426c57b0 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -162,21 +162,4 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch); struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q); struct xe_lrc *xe_exec_queue_get_lrc(struct xe_exec_queue *q, u16 idx); -/** - * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend - * @q: The exec_queue - * - * If an exec queue is not parallel and is idle, the suspend steps can be - * skipped in the submission backend immediatley signaling the suspend fence. - * Parallel queues cannot skip this step due to limitations in the submission - * backend. - * - * Return: True if exec queue is idle and can skip suspend steps, False - * otherwise - */ -static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q); -} - #endif diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 912182dc7704..3db627b56e11 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -71,7 +71,6 @@ exec_queue_to_guc(struct xe_exec_queue *q) #define EXEC_QUEUE_STATE_WEDGED (1 << 8) #define EXEC_QUEUE_STATE_BANNED (1 << 9) #define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10) -#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11) static bool exec_queue_registered(struct xe_exec_queue *q) { @@ -218,21 +217,6 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q) atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state); } -static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND; -} - -static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - -static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) { return (atomic_read(&q->guc->state) & @@ -1153,7 +1137,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (!job->restore_replay || job->last_replay) { if (xe_exec_queue_is_parallel(q)) wq_item_append(q); - else if (!exec_queue_idle_skip_suspend(q)) + else xe_lrc_set_ring_tail(lrc, lrc->ring.tail); job->last_replay = false; } @@ -1810,10 +1794,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; struct xe_guc *guc = exec_queue_to_guc(q); - bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) && - !exec_queue_suspended(q) && exec_queue_enabled(q)) { + if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && + exec_queue_enabled(q)) { wait_event(guc->ct.wq, vf_recovery(guc) || ((q->guc->resume_time != RESUME_PENDING || xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q))); @@ -1832,33 +1815,11 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) disable_scheduling(q, false); } } else if (q->guc->suspend_pending) { - if (idle_skip_suspend) - set_exec_queue_idle_skip_suspend(q); set_exec_queue_suspended(q); suspend_fence_signal(q); } } -static void sched_context(struct xe_exec_queue *q) -{ - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_lrc *lrc = q->lrc[0]; - u32 action[] = { - XE_GUC_ACTION_SCHED_CONTEXT, - q->guc->id, - }; - - xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - - trace_xe_exec_queue_submit(q); - - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0); -} - static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; @@ -1866,22 +1827,12 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q)) { clear_exec_queue_suspended(q); if (!exec_queue_enabled(q)) { - if (exec_queue_idle_skip_suspend(q)) { - struct xe_lrc *lrc = q->lrc[0]; - - clear_exec_queue_idle_skip_suspend(q); - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - } q->guc->resume_time = RESUME_PENDING; set_exec_queue_pending_resume(q); enable_scheduling(q); - } else if (exec_queue_idle_skip_suspend(q)) { - clear_exec_queue_idle_skip_suspend(q); - sched_context(q); } } else { clear_exec_queue_suspended(q); - clear_exec_queue_idle_skip_suspend(q); } } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c index 4c2b113364d3..02cf32ae5aa9 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c @@ -208,21 +208,15 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group lockdep_assert_held_write(&group->mode_sem); list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { - bool idle_skip_suspend; if (!xe_vm_in_fault_mode(q->vm)) continue; - idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && has_deps) + if (has_deps) return -EAGAIN; xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); - if (idle_skip_suspend) - xe_gt_stats_incr(q->gt, - XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1); - - need_resume |= !idle_skip_suspend; + need_resume = true; q->ops->suspend(q); gt = q->gt; } -- cgit v1.2.3 From 54f2a0442a30fe7a0f6bc8345e81f8b2db8effbd Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:16 +0530 Subject: drm/xe: Clear pending_disable before signaling suspend fence In the schedule-disable done path for suspend, we signal the suspend fence before clearing pending_disable. That wakeup can let suspend_wait complete and resume be queued immediately. The resume path may then reach enable_scheduling() while pending_disable is still set and hit the !exec_queue_pending_disable(q) assertion. Fix this by clearing pending_disable before signaling the suspend fence, so any resumed transition observes a consistent state. Fixes: 87651f31ae4e ("drm/xe/guc_submit: fix race around suspend_pending") Cc: stable@vger.kernel.org # v7.0+ Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-3-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 4b1ae138b0e103d753773956a84eebc2edbf62c4) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 3db627b56e11..3493dd533d6c 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -2804,8 +2804,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); if (q->guc->suspend_pending) { - suspend_fence_signal(q); clear_exec_queue_pending_disable(q); + suspend_fence_signal(q); } else { if (exec_queue_banned(q)) { smp_wmb(); -- cgit v1.2.3 From ec4cbdd163f9bb2a2bd44eb93ecf4a2fa0e912a9 Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Wed, 3 Jun 2026 16:39:47 -0700 Subject: drm/xe/multi_queue: skip submit when primary queue is suspended Return early in submit path when the multi-queue primary exec queue is suspended to avoid submitting while suspended. v2: Remove idle_skip_suspend fix as that feature is being reverted here https://patchwork.freedesktop.org/series/167262/ Fixes: bc5775c59258 ("drm/xe/multi_queue: Add GuC interface for multi queue support") Cc: stable@vger.kernel.org # v7.0+ Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Reviewed-by: Daniele Ceraolo Spurio Signed-off-by: Niranjana Vishwanathapura Link: https://patch.msgid.link/20260603233946.863663-2-niranjana.vishwanathapura@intel.com (cherry picked from commit b7fb55cc3364ca128cfff9d50649ffd4327cd01e) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_submit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 3493dd533d6c..a4a8f0d41fe8 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1147,9 +1147,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) /* * All queues in a multi-queue group will use the primary queue - * of the group to interface with GuC. + * of the group to interface with GuC. If primay is suspended, + * just return. Jobs will get scheduled once primary is resumed. */ q = xe_exec_queue_multi_queue_primary(q); + if (exec_queue_suspended(q)) + return; if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; -- cgit v1.2.3 From 6bf7e2affc6e62da7add393d7f352d4040f5bc27 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sun, 31 May 2026 17:18:55 -0300 Subject: drm/v3d: Fix global performance monitor reference counting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the SET_GLOBAL ioctl, v3d_perfmon_find() bumps the reference count on the perfmon it returns, but v3d_perfmon_set_global_ioctl() and v3d_perfmon_delete() fail to release that reference on several paths: 1. v3d_perfmon_set_global_ioctl() leaks the reference on its error paths. 2. CLEAR_GLOBAL leaks both the find reference and the reference previously stashed in v3d->global_perfmon by the SET_GLOBAL ioctl that configured it. 3. Destroying a perfmon that is the current global perfmon leaks the reference stashed by the SET_GLOBAL ioctl. Release each of these references explicitly. Cc: stable@vger.kernel.org Fixes: c6eabbab359c ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL") Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260531-v3d-perfmon-lifetime-v2-1-60ed4485a203@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_perfmon.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 8e0249580bba..ecfd446ff75f 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -309,8 +309,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv, if (perfmon == v3d->active_perfmon) v3d_perfmon_stop(v3d, perfmon, false); - /* If the global perfmon is being destroyed, set it to NULL */ - cmpxchg(&v3d->global_perfmon, perfmon, NULL); + /* If the global perfmon is being destroyed, clean it and release + * the reference stashed in v3d_perfmon_set_global_ioctl(). + */ + if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon) + v3d_perfmon_put(perfmon); v3d_perfmon_put(perfmon); } @@ -461,16 +464,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data, /* If the request is to clear the global performance monitor */ if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) { - if (!v3d->global_perfmon) + struct v3d_perfmon *old; + + /* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if + * v3d->global_perfmon == perfmon. Therefore, there + * is no need to keep perfmon's reference. + */ + v3d_perfmon_put(perfmon); + + old = xchg(&v3d->global_perfmon, NULL); + if (!old) return -EINVAL; - xchg(&v3d->global_perfmon, NULL); + v3d_perfmon_put(old); return 0; } - if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) + if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) { + v3d_perfmon_put(perfmon); return -EBUSY; + } return 0; } -- cgit v1.2.3 From 00f547e0dfecf83014fb32bcba587c6b684c1362 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 19:51:59 +0000 Subject: accel/ethosu: fix IFM region index out-of-bounds in command stream parser NPU_SET_IFM_REGION extracts the region index with param & 0x7f, giving a maximum value of 127. However region_size[] and output_region[] in struct ethosu_validated_cmdstream_info are both sized to NPU_BASEP_REGION_MAX (8), giving valid indices [0..7]. Every other region assignment in the same switch uses param & 0x7: NPU_SET_OFM_REGION: st.ofm.region = param & 0x7; NPU_SET_IFM2_REGION: st.ifm2.region = param & 0x7; NPU_SET_WEIGHT_REGION: st.weight[0].region = param & 0x7; NPU_SET_SCALE_REGION: st.scale[0].region = param & 0x7; The 0x7f mask on IFM is inconsistent and appears to be a typo. feat_matrix_length() and calc_sizes() use the region index directly as an array subscript into the kzalloc'd info struct: info->region_size[fm->region] = max(...); A userspace caller supplying NPU_SET_IFM_REGION with param > 7 causes a write up to 127*8 = 1016 bytes past the start of region_size[], corrupting adjacent kernel heap data. Fix by applying the same & 0x7 mask used by all other region assignments. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523195159.55801-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 7994e7073903..ced99cf9cdfc 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -464,7 +464,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, st.ifm.broadcast = param; break; case NPU_SET_IFM_REGION: - st.ifm.region = param & 0x7f; + st.ifm.region = param & 0x7; break; case NPU_SET_IFM_WIDTH0_M1: st.ifm.width0 = param; -- cgit v1.2.3 From ef911805d86a05363d3ec2fa9835a41def83bb7e Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 21:07:52 +0000 Subject: accel/ethosu: reject NPU_OP_RESIZE commands from userspace NPU_OP_RESIZE is a U85-only command that the driver does not yet implement. The existing WARN_ON(1) placeholder fires unconditionally whenever userspace submits this command via DRM_IOCTL_ETHOSU_GEM_CREATE, causing unbounded kernel log spam. If panic_on_warn is set the kernel panics, giving any unprivileged user with access to the DRM device a trivial denial-of-service primitive. Replace the WARN_ON(1) with an explicit -EINVAL return so the ioctl rejects the command before it reaches hardware. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523210840.92039-2-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index ced99cf9cdfc..863cdadb137a 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -431,8 +431,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return ret; break; case NPU_OP_RESIZE: // U85 only - WARN_ON(1); // TODO - break; + return -EINVAL; case NPU_SET_KERNEL_WIDTH_M1: st.ifm.width = param; break; -- cgit v1.2.3 From e703843f242b28e35ac79408de571ae110c740b5 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 21:07:53 +0000 Subject: accel/ethosu: fix wrong weight index in NPU_SET_SCALE1_LENGTH on U85 On non-U65 hardware (e.g. U85), opcode 0x4093 is NPU_SET_WEIGHT2_LENGTH. The BASE handler for the same opcode correctly assigns to st.weight[2].base, but the LENGTH handler mistakenly assigns cmds[1] to st.weight[1].length instead of st.weight[2].length. This leaves weight[2].length at its initialised sentinel value of 0xffffffff and corrupts weight[1].length with the user-supplied value, breaking the software bounds-check state for both weight buffers on U85. Fix the index to match the BASE handler. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523210840.92039-3-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 863cdadb137a..52b6a8752c75 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -598,7 +598,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, if (ethosu_is_u65(edev)) st.scale[1].length = cmds[1]; else - st.weight[1].length = cmds[1]; + st.weight[2].length = cmds[1]; break; case NPU_SET_WEIGHT3_BASE: st.weight[3].base = addr; -- cgit v1.2.3 From ee6d9b6e51626f259c6f0e38d94f91be4fd14754 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 24 May 2026 10:37:10 +0000 Subject: accel/ethosu: fix arithmetic issues in dma_length() dma_length() derives DMA region usage from command stream values and updates region_size[]: len = ((len + stride[0]) * size0 + stride[1]) * size1 region_size[region] = max(..., len + dma->offset) Several arithmetic issues can corrupt the derived region size: - signed stride values may underflow when added to len - intermediate multiplications may overflow - len + dma->offset may overflow during region_size updates - dma_length() error returns were not validated by the caller region_size[] is later used by ethosu_job.c to validate command stream accesses against GEM buffer sizes. Arithmetic wraparound can therefore under-report region usage and bypass the bounds validation. Fix by validating signed additions, using overflow helpers for multiplications and offset updates, and propagating dma_length() failures to the caller. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260524103710.47397-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 52b6a8752c75..27c5fdbe9a5d 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -2,6 +2,7 @@ /* Copyright 2025 Arm, Ltd. */ #include +#include #include #include @@ -164,16 +165,26 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, u64 len = dma->len; if (mode >= 1) { + if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) + return U64_MAX; len += dma->stride[0]; - len *= dma_st->size0; + if (check_mul_overflow(len, (u64)dma_st->size0, &len)) + return U64_MAX; } if (mode == 2) { + if (dma->stride[1] < 0 && (u64)(-dma->stride[1]) > len) + return U64_MAX; len += dma->stride[1]; - len *= dma_st->size1; + if (check_mul_overflow(len, (u64)dma_st->size1, &len)) + return U64_MAX; + } + if (dma->region >= 0) { + u64 end; + + if (check_add_overflow(len, dma->offset, &end)) + return U64_MAX; + info->region_size[dma->region] = max(info->region_size[dma->region], end); } - if (dma->region >= 0) - info->region_size[dma->region] = max(info->region_size[dma->region], - len + dma->offset); return len; } @@ -395,6 +406,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, case NPU_OP_DMA_START: srclen = dma_length(info, &st.dma, &st.dma.src); dstlen = dma_length(info, &st.dma, &st.dma.dst); + if (srclen == U64_MAX || dstlen == U64_MAX) + return -EINVAL; if (st.dma.dst.region >= 0) info->output_region[st.dma.dst.region] = true; -- cgit v1.2.3 From d9d021218162b6c4fe0bdf42b2b340f1aae23a12 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 24 May 2026 13:03:19 +0000 Subject: accel/ethosu: reject DMA commands with uninitialized length cmd_state_init() initializes the command state with memset(0xff), leaving dma->len at U64_MAX to signal missing setup. The only setter is NPU_SET_DMA0_LEN; if userspace omits this command and issues NPU_OP_DMA_START, dma->len remains U64_MAX. In dma_length(), a positive stride added to U64_MAX wraps to a small value. With size0 == 1, check_mul_overflow() does not trigger and dma_length() returns 0 instead of U64_MAX. The caller's U64_MAX check then passes, region_size[] stays 0, and the bounds check in ethosu_job.c is bypassed, allowing hardware to execute DMA with stale physical addresses. Fix by checking for U64_MAX at the start of dma_length() before any arithmetic, consistent with the sentinel value used throughout the driver to detect uninitialized fields. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260524130319.12747-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 27c5fdbe9a5d..2cb7964ddfa5 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -164,6 +164,9 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, s8 mode = dma_st->mode; u64 len = dma->len; + if (len == U64_MAX) + return U64_MAX; + if (mode >= 1) { if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) return U64_MAX; -- cgit v1.2.3 From 4aacf509e537a711fa71bca9f234e5eb6968850e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 2 Jun 2026 09:34:14 +0200 Subject: net: mv643xx: fix OF node refcount Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 76723bca2802 ("net: mv643xx_eth: add DT parsing support") Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260602073414.22500-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index f9055b3d6fb1..1881583be5ce 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, goto put_err; } ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - ppdev->dev.of_node = pnp; + ppdev->dev.of_node = of_node_get(pnp); ret = platform_device_add_resources(ppdev, &res, 1); if (ret) -- cgit v1.2.3 From c0837b9cf6eabbad8b8cbddaff1a46a6d0a2e29d Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sat, 23 May 2026 19:08:43 +0000 Subject: accel/ethosu: fix OOB write in ethosu_gem_cmdstream_copy_and_validate() The command stream parsing loop increments the index variable a second time when a 64-bit command word is encountered (bit 14 set), but does not re-check the loop bound before writing the second word: for (i = 0; i < size / 4; i++) { bocmds[i] = cmds[0]; if (cmd & 0x4000) { i++; bocmds[i] = cmds[1]; /* unchecked */ } } The buffer bocmds is backed by a DMA allocation of exactly size bytes from drm_gem_dma_create(ddev, size), giving valid indices [0, size/4-1]. When i == size/4 - 1 on entry to an iteration and bit 14 of cmds[0] is set, bocmds[size/4-1] is written in bounds, i is then incremented to size/4, and bocmds[size/4] writes four bytes past the end of the allocation. Userspace controls both the buffer contents and the size argument via the ioctl, making this a userspace-triggerable heap out-of-bounds write. Fix by checking the incremented index against the buffer bound before the second write and returning -EINVAL if the buffer is too small to contain the extended command. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260523190843.33977-1-meatuni001@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 2cb7964ddfa5..3401883e207f 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -401,6 +401,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return -EFAULT; i++; + if (i >= size / 4) + return -EINVAL; bocmds[i] = cmds[1]; addr = cmd_to_addr(cmds); } -- cgit v1.2.3 From 2914709c914101eb704e01bed2351070d4161ccf Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Sun, 17 May 2026 08:17:09 +0530 Subject: Revert "drm/i915/backlight: Remove try_vesa_interface" This reverts commit 40d2f5820951dee818d05c14677277048bd85f9f. Removing the try_vesa_interface gate caused a backlight regression on panels whose VBT correctly reports INTEL_BACKLIGHT_DISPLAY_DDI and whose PWM path is the actual backlight control, but whose DPCD optimistically advertises DP_EDP_BACKLIGHT_AUX_ENABLE_CAP / _BRIGHTNESS_AUX_SET_CAP. After the commit such panels silently bind to the VESA AUX backlight funcs; AUX writes complete but the panel ignores them, leaving brightness stuck (no-op backlight). Observed on at least KBL and TGL eDP setups. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260517024709.1016121-1-suraj.kandpal@intel.com (cherry picked from commit f30fddb4402313aa5301a74d721638d343395269) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index a8d56ebf06a2..7a6c07f6aaeb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -691,10 +691,9 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = connector->base.dev; struct intel_panel *panel = &connector->panel; - bool try_intel_interface = false; + bool try_intel_interface = false, try_vesa_interface = false; - /* - * Check the VBT and user's module parameters to figure out which + /* Check the VBT and user's module parameters to figure out which * interfaces to probe */ switch (display->params.enable_dpcd_backlight) { @@ -703,6 +702,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) case INTEL_DP_AUX_BACKLIGHT_AUTO: switch (panel->vbt.backlight.type) { case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE: + try_vesa_interface = true; break; case INTEL_BACKLIGHT_DISPLAY_DDI: try_intel_interface = true; @@ -715,12 +715,20 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE) try_intel_interface = true; + try_vesa_interface = true; + break; + case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA: + try_vesa_interface = true; break; case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: try_intel_interface = true; break; } + /* For eDP 1.5 and above we are supposed to use VESA interface for brightness control */ + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + try_vesa_interface = true; + /* * Since Intel has their own backlight control interface, the majority of machines out there * using DPCD backlight controls with Intel GPUs will be using this interface as opposed to @@ -733,9 +741,6 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) * panel with Intel's OUI - which is also required for us to be able to detect Intel's * backlight interface at all. This means that the only sensible way for us to detect both * interfaces is to probe for Intel's first, and VESA's second. - * - * Also there is a chance some VBTs may advertise false Intel backlight support even if the - * TCON DPCD says otherwise. This means we keep VESA interface as fallback in that case. */ if (try_intel_interface && intel_dp->edp_dpcd[0] <= DP_EDP_14b && intel_dp_aux_supports_hdr_backlight(connector)) { @@ -745,7 +750,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) return 0; } - if (intel_dp_aux_supports_vesa_backlight(connector)) { + if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) { drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using VESA eDP backlight controls\n", connector->base.base.id, connector->base.name); panel->backlight.funcs = &intel_dp_vesa_bl_funcs; -- cgit v1.2.3 From 1a4f03d22fb655e5f192244fb2c87d8066fcfca2 Mon Sep 17 00:00:00 2001 From: Simona Vetter Date: Thu, 4 Jun 2026 21:44:37 +0200 Subject: drm/gem: Try to fix change_handle ioctl, attempt 4 [airlied: just added some comments on how to reenable] On-list because the cat is out of the bag and we're clearly not good enough to figure this out in private. The story thus far: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") tried to fix a race condition between the gem_close and gem_change_handle ioctls, but got a few things wrong: - There's a confusion with the local variable handle, which is actually the new handle, and so the two-stage trick was actually applied to the wrong idr slot. 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete") tried to fix that by adding yet another code block, but forgot to add the error handling. Which meant we now have two paths, both kinda wrong. - dc366607c41c ("drm: Replace old pointer to new idr") tried to apply another fix, but inconsistently, again because of the handle confusion - this would be the right fix (kinda, somewhat, it's a mess) if we'd do the two-stage approach for the new handle. Except that wasn't the intent of the original fix. We also didn't have an igt merged for the original ioctl, which is a big no-go. This was attempted to address off-list in the original bugfix, and amd QA people claimed the bug was fixed now. Very clearly that's not the case. Here's my attempt to sort this out: - Rename the local variable to new_handle, the old aliasing with args->handle is just too dangerously confusing. - Merge the gem obj lookup with the two-stage idr_replace so that we avoid getting ourselves confused there. - This means we don't have a surplus temporary reference anymore, only an inherited from the idr. A concurrent gem_close on the new_handle could steal that. Fix that with the same two-stage approach create_tail uses. This is a bit overkill as documented in the comment, but I also don't trust my ability to understand this all correctly, so go with the established pattern we have from other ioctls instead for maximum paranoia. - Adjust error paths. I've tried to make the error and success paths common, because they are identical except for which handle is removed and on which we call idr_replace to (re)install the object again. But that made things messier to read, so I've left it at the more verbose version, which unfortunately hides the symmetry in the entire code flow a bit. - While at it, also replace the 7 space indent with 1 tab. And finally, because I flat out don't trust my abilities here at all anymore: - Disable the ioctl until we have the igt situation and everything else sorted out on-list and with full consensus. v2: Sashiko noticed that I didn't handle the error path for idr_replace correctly, it must be checked with IS_ERR_OR_NULL like in gem_handle_delete. So yeah, definitely should just the existing paths 1:1 because this is endless amounts of tricky. Also add the Fixes: line for the original ioctl, I forgot that too. Reported-by: DARKNAVY (@DarkNavyOrg) Signed-off-by: Simona Vetter Fixes: dc366607c41c ("drm: Replace old pointer to new idr") Cc: syzbot+d7c9eed171647e421013@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Cc: Edward Adam Davis Cc: Dave Airlie Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Cc: David Francis Cc: Puttimet Thammasaeng Cc: Christian Koenig Fixes: 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete") Cc: Zhenghang Xiao Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Reviewed-by: David Francis Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260604194437.1725314-1-simona.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem.c | 75 +++++++++++++++++++++------------------------ drivers/gpu/drm/drm_ioctl.c | 3 +- 2 files changed, 37 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e12cdf91f4dc..3b2448a3a9de 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1015,12 +1015,25 @@ err: return ret; } +/* + * This ioctl is disabled for security reasons but also it failed + * to follow process in terms of adding testing in igt and verifying + * all the corner cases which made fixing security bugs in it even + * harder than necessary. + * + * To re-enable this ioctl + * 1. land working IGT tests in igt-gpu-tools that cover + * all corner cases and race conditions. + * 2. handle idr_preload + * 3. handle == 0 + * 4. handle == new_handle semantics definition. + */ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_gem_change_handle *args = data; - struct drm_gem_object *obj, *idrobj; - int handle, ret; + struct drm_gem_object *obj; + int new_handle, ret; if (!drm_core_check_feature(dev, DRIVER_GEM)) return -EOPNOTSUPP; @@ -1028,52 +1041,36 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, /* idr_alloc() limitation. */ if (args->new_handle > INT_MAX) return -EINVAL; - handle = args->new_handle; - - obj = drm_gem_object_lookup(file_priv, args->handle); - if (!obj) - return -ENOENT; + new_handle = args->new_handle; - if (args->handle == handle) { - ret = 0; - goto out; - } + if (args->handle == new_handle) + return 0; mutex_lock(&file_priv->prime.lock); - spin_lock(&file_priv->table_lock); - - /* When create_tail allocs an obj idr, it needs to first alloc as NULL, - * then later replace with the correct object. This is not necessary - * here, because the only operations that could race are drm_prime - * bookkeeping, and we hold the prime lock. - */ - ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1, + ret = idr_alloc(&file_priv->object_idr, NULL, new_handle, new_handle + 1, GFP_NOWAIT); - if (ret < 0) { - spin_unlock(&file_priv->table_lock); - goto out_unlock; - } - - idrobj = idr_replace(&file_priv->object_idr, NULL, handle); - if (idrobj != obj) { - idr_replace(&file_priv->object_idr, idrobj, handle); - idr_remove(&file_priv->object_idr, args->new_handle); - spin_unlock(&file_priv->table_lock); - ret = -ENOENT; - goto out_unlock; - } - - idr_replace(&file_priv->object_idr, NULL, args->handle); + if (ret < 0) { + spin_unlock(&file_priv->table_lock); + goto out_unlock; + } + + obj = idr_replace(&file_priv->object_idr, NULL, args->handle); + if (IS_ERR_OR_NULL(obj)) { + idr_remove(&file_priv->object_idr, new_handle); + spin_unlock(&file_priv->table_lock); + ret = -ENOENT; + goto out_unlock; + } spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, - handle); + new_handle); if (ret < 0) { spin_lock(&file_priv->table_lock); - idr_remove(&file_priv->object_idr, handle); + idr_remove(&file_priv->object_idr, new_handle); idr_replace(&file_priv->object_idr, obj, args->handle); spin_unlock(&file_priv->table_lock); goto out_unlock; @@ -1086,14 +1083,12 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, args->handle); - idrobj = idr_replace(&file_priv->object_idr, obj, handle); + obj = idr_replace(&file_priv->object_idr, obj, new_handle); spin_unlock(&file_priv->table_lock); - WARN_ON(idrobj != NULL); + WARN_ON(obj != NULL); out_unlock: mutex_unlock(&file_priv->prime.lock); -out: - drm_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index ff193155129e..e2df4becce62 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -660,7 +660,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW), + /* see drm_gem.c:drm_gem_change_handle_ioctl for why this is invalid */ + DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_invalid_op, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0), -- cgit v1.2.3 From f1fa677e428e8873486938086bd934dc18169b47 Mon Sep 17 00:00:00 2001 From: Petr Oros Date: Tue, 2 Jun 2026 15:55:10 -0700 Subject: ice: fix missing priority callbacks for U.FL DPLL pins The U.FL2 input pin advertises DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE in its capability mask, but ice_dpll_pin_ufl_ops does not provide .prio_get and .prio_set callbacks. As a result the DPLL subsystem cannot report or accept priority for U.FL pins: pin-get omits the prio field on U.FL2 and pin-set with prio is rejected as invalid, even though the capability is present. This prevents user space from using priority to select or disable U.FL2 as a DPLL input source. Reproducer with iproute2 (dpll command): # dpll pin show board-label U.FL2 pin id 16: module-name ice board-label U.FL2 type ext capabilities priority-can-change|state-can-change parent-device: id 0 direction input state selectable phase-offset 0 /* note: no "prio" between "direction" and "state", even though priority-can-change is advertised */ # dpll pin set id 16 parent-device 0 prio 5 RTNETLINK answers: Operation not supported After the fix the prio field is reported by pin show and pin set with prio is accepted on U.FL2. Add the missing .prio_get and .prio_set callbacks to ice_dpll_pin_ufl_ops, reusing ice_dpll_sw_input_prio_{get,set}. The same ops struct is shared by U.FL1 and U.FL2: U.FL2 (input) delegates to the backing hardware input pin, while U.FL1 (output) does not advertise DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE so the dpll core capability gate never invokes prio_set for it, and prio_get reports the OUTPUT sentinel (ICE_DPLL_PIN_PRIO_OUTPUT) on the output side exactly like the SMA path does today. Fixes: 2dd5d03c77e2 ("ice: redesign dpll sma/u.fl pins control") Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Signed-off-by: Petr Oros Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260602225513.393338-3-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_dpll.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 892bc7c2e28b..0704e92ab043 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -2633,6 +2633,8 @@ static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = { .state_on_dpll_set = ice_dpll_ufl_pin_state_set, .state_on_dpll_get = ice_dpll_sw_pin_state_get, .direction_get = ice_dpll_pin_sw_direction_get, + .prio_get = ice_dpll_sw_input_prio_get, + .prio_set = ice_dpll_sw_input_prio_set, .frequency_get = ice_dpll_sw_pin_frequency_get, .frequency_set = ice_dpll_sw_pin_frequency_set, .esync_set = ice_dpll_sw_esync_set, -- cgit v1.2.3 From 85b0cbc1f38bc1e38956a9e6d7b04d309b435697 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Tue, 2 Jun 2026 15:55:11 -0700 Subject: idpf: fix mailbox capability for set device clock time The current code incorrectly uses VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME for both direct and mailbox capabilities, causing mailbox-only support to be ignored and potentially reporting IDPF_PTP_NONE. Fixes: d5dba8f7206da ("idpf: add PTP clock configuration") Signed-off-by: Alok Tiwari Tested-by: Samuel Salin Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260602225513.393338-4-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/idpf/idpf_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index 4a51d2727547..71fe8b2a8b4e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -51,7 +51,7 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter) /* Set the device clock time */ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; - mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; + mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB; ptp->set_dev_clk_time_access = idpf_ptp_get_access(adapter, direct, mailbox); -- cgit v1.2.3 From f9f25118faa4dd2b6e3d14a03d123bbdbd59925d Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 4 Jun 2026 15:03:52 +0800 Subject: net: airoha: Add NULL check for of_reserved_mem_lookup() in airoha_qdma_init_hfwd_queues() of_reserved_mem_lookup() may return NULL if the reserved memory region referenced by the "memory-region" phandle is not found in the reserved memory table (e.g. due to a misconfigured DTS or a removed memory-region node). The current code dereferences the returned pointer without checking for NULL, leading to a kernel NULL pointer dereference at the following lines: dma_addr = rmem->base; // line 1156 num_desc = div_u64(rmem->size, buf_size); // line 1160 Add a NULL check after of_reserved_mem_lookup() and return -ENODEV if the lookup fails, which is consistent with the existing error handling for of_parse_phandle() failure in the same code block. Fixes: 3a1ce9e3d01b ("net: airoha: Add the capability to allocate hwfd buffers via reserved-memory") Cc: stable@vger.kernel.org Signed-off-by: ZhaoJinming Acked-by: Lorenzo Bianconi Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index eab6a98d62b9..31cdb11cd78d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1153,6 +1153,9 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) rmem = of_reserved_mem_lookup(np); of_node_put(np); + if (!rmem) + return -ENODEV; + dma_addr = rmem->base; /* Compute the number of hw descriptors according to the * reserved memory size and the payload buffer size -- cgit v1.2.3 From 954981dbbfbd78f21d2fbac1ac0742dbf38b4e69 Mon Sep 17 00:00:00 2001 From: Arthur Kiyanovski Date: Thu, 4 Jun 2026 08:07:04 +0000 Subject: net: ena: PHC: Add missing barrier Add dma_rmb() barrier after req_id completion check in ena_com_phc_get_timestamp(). On weakly-ordered architectures, payload fields may be read before req_id is observed as updated. Fixes: e0ea34158ee8 ("net: ena: Add PHC support in the ENA driver") Closes: https://sashiko.dev/#/patchset/20260430032507.11586-1-akiyano%40amazon.com Signed-off-by: Arthur Kiyanovski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amazon/ena/ena_com.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 8c86789d867a..297fb36ab8c1 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -1880,6 +1880,11 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp) continue; } + /* Ensure PHC payload (timestamp, error_flags) is read + * after req_id update is observed + */ + dma_rmb(); + /* req_id was updated by the device which indicates that * PHC timestamp and error_flags are updated too, * checking errors before retrieving timestamp -- cgit v1.2.3 From 3847d94783c0b893c27ff0b26a3325796d9444c6 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Thu, 4 Jun 2026 22:07:09 +0530 Subject: bnge: fix context mem iteration The firmware advertises context memory (backing store) types through a linked list, with BNGE_CTX_INV serving as the end-of-list sentinel. However, the driver incorrectly assumes that the list is strictly ordered and prematurely terminates traversal when it encounters an unrecognized type (>=BNGE_CTX_V2_MAX). As a result, any valid context types that appear later in the chain are silently skipped, leading to incomplete memory configuration and eventual driver load failure. Fix this by traversing the entire list until the BNGE_CTX_INV sentinel is reached, while safely ignoring only those context types that fall outside the supported range. Fixes: 29c5b358f385 ("bng_en: Add backing store support") Signed-off-by: Vikas Gupta Reviewed-by: Dharmender Garg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c index eb11800f5573..1c9cfec1b633 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c @@ -277,7 +277,7 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) struct hwrm_func_backing_store_qcaps_v2_output *resp; struct hwrm_func_backing_store_qcaps_v2_input *req; struct bnge_ctx_mem_info *ctx; - u16 type; + u16 type, next_type; int rc; if (bd->ctx) @@ -294,8 +294,8 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) resp = bnge_hwrm_req_hold(bd, req); - for (type = 0; type < BNGE_CTX_V2_MAX; ) { - struct bnge_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; + for (type = 0; type < BNGE_CTX_INV; type = next_type) { + struct bnge_ctx_mem_type *ctxm; u8 init_val, init_off, i; __le32 *p; u32 flags; @@ -304,8 +304,14 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) rc = bnge_hwrm_req_send(bd, req); if (rc) goto ctx_done; + + next_type = le16_to_cpu(resp->next_valid_type); + if (type >= BNGE_CTX_V2_MAX) + continue; + + ctxm = &ctx->ctx_arr[type]; flags = le32_to_cpu(resp->flags); - type = le16_to_cpu(resp->next_valid_type); + if (!(flags & FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_TYPE_VALID)) continue; -- cgit v1.2.3 From fb402386af4cdce108ff991a796386de55439735 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Fri, 5 Jun 2026 15:27:21 +0800 Subject: Input: atkbd - skip deactivate for HONOR BCC-N's internal keyboard After commit 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID"), HONOR BCC-N, aka HONOR MagicBook 14 2026's internal keyboard stops working. Adding the atkbd_deactivate_fixup quirk fixes it. DMI: HONOR BCC-N/BCC-N-PCB, BIOS 1.04 04/07/2026 Fixes: 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID") Reported-by: Hongfei Ren Link: https://github.com/colorcube/Linux-on-Honor-Magicbook-14-Pro/issues/1#issuecomment-4562679891 Tested-by: Hongfei Ren Cc: stable@kernel.org Signed-off-by: Cryolitia PukNgae Link: https://patch.msgid.link/20260605-honor-v1-1-78e05e491193@linux.dev Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 217e66ee36a1..8cb4dc6fb165 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1931,6 +1931,13 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; -- cgit v1.2.3 From b9ad50d7505ebd48282ec3630258dc820fc85c81 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 8 Jun 2026 16:43:34 +0800 Subject: gpio: mvebu: fix NULL pointer dereference in suspend/resume mvebu_pwm_suspend() and mvebu_pwm_resume() are called for all GPIO banks during suspend/resume, but not all banks have PWM functionality. GPIO banks without PWM have mvchip->mvpwm set to NULL. Calling mvebu_pwm_suspend() with mvpwm == NULL causes a NULL pointer dereference when it tries to access mvpwm->blink_select. Unable to handle kernel NULL pointer dereference at virtual address 00000020 when write [00000020] *pgd=00000000 Internal error: Oops: 815 [#1] PREEMPT ARM Modules linked in: CPU: 0 UID: 0 PID: 406 Comm: sh Not tainted 6.12.74-rt12-yocto-standard-g4e96f98fb7db-dirty #353 Hardware name: Marvell Armada 370/XP (Device Tree) PC is at regmap_mmio_read+0x38/0x54 LR is at regmap_mmio_read+0x38/0x54 pc : [] lr : [] psr: 200f0013 sp : f0c11d10 ip : 00000000 fp : c100d2f0 r10: c14fb854 r9 : 00000000 r8 : 00000000 r7 : c1799c00 r6 : 00000020 r5 : 00000020 r4 : c179c7c0 r3 : f0a231a0 r2 : 00000020 r1 : 00000020 r0 : 00000000 Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 135ec059 DAC: 00000051 Call trace: regmap_mmio_read from _regmap_bus_reg_read+0x78/0xac _regmap_bus_reg_read from _regmap_read+0x60/0x154 _regmap_read from regmap_read+0x3c/0x60 regmap_read from mvebu_gpio_suspend+0xa4/0x14c mvebu_gpio_suspend from dpm_run_callback+0x54/0x180 dpm_run_callback from device_suspend+0x124/0x630 device_suspend from dpm_suspend+0x124/0x270 dpm_suspend from dpm_suspend_start+0x64/0x6c dpm_suspend_start from suspend_devices_and_enter+0x140/0x8e8 suspend_devices_and_enter from pm_suspend+0x2fc/0x308 pm_suspend from state_store+0x6c/0xc8 state_store from kernfs_fop_write_iter+0x10c/0x1f8 kernfs_fop_write_iter from vfs_write+0x270/0x468 vfs_write from ksys_write+0x70/0xf0 ksys_write from ret_fast_syscall+0x0/0x54 Add a NULL check for mvchip->mvpwm before calling the PWM suspend/resume functions. Fixes: 757642f9a584 ("gpio: mvebu: Add limited PWM support") Signed-off-by: Yun Zhou Link: https://patch.msgid.link/20260608084334.2960803-1-yun.zhou@windriver.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 22c36b79e249..c030d1f00abc 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -996,7 +996,7 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_suspend(mvchip); return 0; @@ -1048,7 +1048,7 @@ static int mvebu_gpio_resume(struct platform_device *pdev) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_resume(mvchip); return 0; -- cgit v1.2.3 From badad6fad60def1b9805559dd81dbab3d97b82aa Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 4 Jun 2026 15:03:13 -0300 Subject: RDMA: During rereg_mr ensure that REREG_ACCESS is compatible If IB_MR_REREG_ACCESS changes from RO to RW then the umem has to be re-evaluated to ensure it is properly pinned as RW. Since the umem is hidden inside each driver's mr struct add a ib_umem_check_rereg() function that each driver has to call before processing IB_MR_REREG_ACCESS. mlx4 has to retain its duplicate ib_access_writable check because it implements IB_MR_REREG_ACCESS | IB_MR_REREG_TRANS by changing both items in place sequentially while the MR is live, so it will continue to not support this combination. Cc: stable@vger.kernel.org Fixes: b40656aa7d55 ("RDMA/umem: remove FOLL_FORCE usage") Link: https://patch.msgid.link/r/0-v1-06fb1a2d6cf5+107-rereg_access_jgg@nvidia.com Reported-by: Philip Tsukerman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem.c | 16 ++++++++++++++++ drivers/infiniband/hw/hns/hns_roce_mr.c | 4 ++++ drivers/infiniband/hw/irdma/verbs.c | 4 ++++ drivers/infiniband/hw/mlx4/mr.c | 4 ++++ drivers/infiniband/hw/mlx5/mr.c | 4 ++++ drivers/infiniband/sw/rxe/rxe_verbs.c | 5 +++++ include/rdma/ib_umem.h | 8 ++++++++ 7 files changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 786fa1aa8e55..4b055712b0d0 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -332,3 +332,19 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, return 0; } EXPORT_SYMBOL(ib_umem_copy_from); + +/* + * Called during rereg mr if the driver is able to re-use a umem for + * IB_MR_REREG_ACCESS. + */ +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags) +{ + if (!umem) + return 0; + + if ((flags & IB_MR_REREG_ACCESS) && !(flags & IB_MR_REREG_TRANS)) + if (ib_access_writable(new_access_flags) && !umem->writable) + return -EACCES; + return 0; +} +EXPORT_SYMBOL(ib_umem_check_rereg); diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 896af1828a38..25bfd3970f5b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -300,6 +300,10 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, goto err_out; } + ret = ib_umem_check_rereg(mr->pbl_mtr.umem, flags, mr_access_flags); + if (ret) + goto err_out; + mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); ret = PTR_ERR_OR_ZERO(mailbox); if (ret) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 17086048d2d7..8cd427532805 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3803,6 +3803,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); + if (ret) + return ERR_PTR(ret); + if (dmabuf_revocable) { umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 650b4a9121ff..6747bca30677 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -209,6 +209,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, struct mlx4_mpt_entry **pmpt_entry = &mpt_entry; int err; + err = ib_umem_check_rereg(mmr->umem, flags, mr_access_flags); + if (err) + return ERR_PTR(err); + /* Since we synchronize this call and mlx4_ib_dereg_mr via uverbs, * we assume that the calls can't run concurrently. Otherwise, a * race exists. diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 3b6da45061a5..fb40b44496f4 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1179,6 +1179,10 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + err = ib_umem_check_rereg(mr->umem, flags, new_access_flags); + if (err) + return ERR_PTR(err); + if (!(flags & IB_MR_REREG_ACCESS)) new_access_flags = mr->access_flags; if (!(flags & IB_MR_REREG_PD)) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 4d4891dc2884..4cf04a44189c 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1319,6 +1319,7 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, struct rxe_mr *mr = to_rmr(ibmr); struct rxe_pd *old_pd = to_rpd(ibmr->pd); struct rxe_pd *pd = to_rpd(ibpd); + int err; /* for now only support the two easy cases: * rereg_pd and rereg_access @@ -1328,6 +1329,10 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, return ERR_PTR(-EOPNOTSUPP); } + err = ib_umem_check_rereg(mr->umem, flags, access); + if (err) + return ERR_PTR(err); + if (flags & IB_MR_REREG_PD) { rxe_put(old_pd); rxe_get(pd); diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 2ad52cc1d52b..49172098a8de 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -156,6 +156,8 @@ void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf); +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags); + #else /* CONFIG_INFINIBAND_USER_MEM */ #include @@ -230,5 +232,11 @@ static inline void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf static inline void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf) {} static inline void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) {} +static inline int ib_umem_check_rereg(struct ib_umem *umem, int flags, + int new_access_flags) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_INFINIBAND_USER_MEM */ #endif /* IB_UMEM_H */ -- cgit v1.2.3 From 29e7b925ae6df64894e82ab6419994dc25580a8a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 15:46:42 -0400 Subject: IB/isert: Reject login PDUs shorter than ISER_HEADERS_LEN In drivers/infiniband/ulp/isert/ib_isert.c, isert_login_recv_done() computes the login request payload length as wc->byte_len minus ISER_HEADERS_LEN with no lower bound, and login_req_len is a signed int. A remote iSER initiator can post a login Send work request carrying fewer than ISER_HEADERS_LEN (76) bytes, so the subtraction underflows and login_req_len becomes negative. isert_rx_login_req() then reads that negative length back into a signed int, takes size = min(rx_buflen, MAX_KEY_VALUE_PAIRS), and because the min() is signed it keeps the negative value; the value is then passed as the memcpy() length and sign-extended to a multi-gigabyte size_t. The copy into the 8192-byte login->req_buf runs far out of bounds and faults, crashing the target node. The login phase precedes iSCSI authentication, so no credentials are required to reach this path. Reject any login PDU shorter than ISER_HEADERS_LEN before the subtraction, mirroring the existing early return on a failed work completion, so login_req_len can never go negative. The upper bound was already safe: a posted login buffer cannot deliver more than ISER_RX_PAYLOAD_SIZE, so the difference stays at or below MAX_KEY_VALUE_PAIRS and the existing min() clamps it; only the missing lower bound needs to be added. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Link: https://patch.msgid.link/r/20260602194642.2273217-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/isert/ib_isert.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 348005e71891..1015a51f750a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1383,6 +1383,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(ib_dev, isert_conn->login_desc->dma_addr, ISER_RX_SIZE, DMA_FROM_DEVICE); + if (unlikely(wc->byte_len < ISER_HEADERS_LEN)) { + isert_dbg("login request length %u is too short\n", + wc->byte_len); + return; + } + isert_conn->login_req_len = wc->byte_len - ISER_HEADERS_LEN; if (isert_conn->conn) { -- cgit v1.2.3 From 13e91fd076306f5d0cdfa14f53d69e37274723c4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 18:04:57 -0400 Subject: RDMA/srp: bound SRP_RSP sense copy by the received length srp_process_rsp() copies sense data from rsp->data + resp_data_len, where resp_data_len is the full 32-bit value supplied by the SRP target and is never checked against the number of bytes actually received (wc->byte_len). The copy length is bounded to SCSI_SENSE_BUFFERSIZE, so at most 96 bytes are copied, but the source offset is not bounded. A malicious or compromised SRP target on the InfiniBand/RoCE fabric that the initiator has logged into can return an SRP_RSP with SRP_RSP_FLAG_SNSVALID set and a large resp_data_len. The receive buffer is allocated at the target-chosen max_ti_iu_len, so the source of the sense copy lands past the bytes actually received; with resp_data_len near 0xFFFFFFFF it is gigabytes past the buffer and the read faults. Copy the sense data only if it has not been truncated, that is, only if the response header, the response data, and the sense region fit within the bytes actually received; otherwise drop the sense and log. The in-tree iSER and NVMe-RDMA receive paths already bound their parse by wc->byte_len; this brings ib_srp into line with them. Fixes: aef9ec39c47f ("IB: Add SCSI RDMA Protocol (SRP) initiator") Link: https://patch.msgid.link/r/20260602220457.2542840-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srp/ib_srp.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index b58868e1cf11..acbd787de265 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1932,7 +1932,8 @@ static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu) return ib_post_recv(ch->qp, &wr, NULL); } -static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) +static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp, + u32 byte_len) { struct srp_target_port *target = ch->target; struct srp_request *req; @@ -1973,10 +1974,27 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) scmnd->result = rsp->status; if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { - memcpy(scmnd->sense_buffer, rsp->data + - be32_to_cpu(rsp->resp_data_len), - min_t(int, be32_to_cpu(rsp->sense_data_len), - SCSI_SENSE_BUFFERSIZE)); + u32 resp_len = be32_to_cpu(rsp->resp_data_len); + u32 sense_len = be32_to_cpu(rsp->sense_data_len); + + /* + * The sense data starts resp_data_len bytes past the + * response data area; both lengths come from the + * target-controlled response. Copy the sense data + * only if it has not been truncated, that is, only if + * the full sense region fits within the bytes actually + * received. Otherwise the copy source would run past + * the receive buffer (sized to the target-chosen + * max_ti_iu_len), reading out of bounds. + */ + if (sizeof(*rsp) + (u64)resp_len + sense_len <= byte_len) + memcpy(scmnd->sense_buffer, + rsp->data + resp_len, + min(sense_len, SCSI_SENSE_BUFFERSIZE)); + else + shost_printk(KERN_ERR, target->scsi_host, + "dropping truncated sense data (resp_data_len %u sense_data_len %u, %u bytes received)\n", + resp_len, sense_len, byte_len); } if (unlikely(rsp->flags & SRP_RSP_FLAG_DIUNDER)) @@ -2086,7 +2104,7 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc) switch (opcode) { case SRP_RSP: - srp_process_rsp(ch, iu->buf); + srp_process_rsp(ch, iu->buf, wc->byte_len); break; case SRP_CRED_REQ: -- cgit v1.2.3 From 2365343f4aad3e1b1e7a2e87e98cf66d5e590589 Mon Sep 17 00:00:00 2001 From: Yao Sang Date: Wed, 3 Jun 2026 14:10:44 +0800 Subject: net/mlx4: avoid GCC 10 __bad_copy_from() false positive mlx4_init_user_cqes() fills a scratch buffer with the CQE initialization pattern and then copies from that buffer to userspace. In the single-copy path, the copy length is array_size(entries, cqe_size), but the scratch buffer is allocated with PAGE_SIZE. GCC 10 does not carry the branch invariant strongly enough through the object size checks and falsely triggers __bad_copy_from(). Size the scratch buffer to the actual copy length for the active path, keep array_size() for the single-copy case, and retain a WARN_ON_ONCE() guard for the PAGE_SIZE invariant before allocating the buffer. Fixes: f69bf5dee7ef ("net/mlx4: Use array_size() helper in copy_to_user()") Signed-off-by: Yao Sang Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/cq.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index e130e7259275..5c55971abbf0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -290,6 +290,7 @@ static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn) static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) { int entries_per_copy = PAGE_SIZE / cqe_size; + size_t copy_bytes; void *init_ents; int err = 0; int i; @@ -314,8 +315,14 @@ static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) buf += PAGE_SIZE; } } else { + copy_bytes = array_size(entries, cqe_size); + if (WARN_ON_ONCE(copy_bytes > PAGE_SIZE)) { + err = -EINVAL; + goto out; + } + err = copy_to_user((void __user *)buf, init_ents, - array_size(entries, cqe_size)) ? + copy_bytes) ? -EFAULT : 0; } -- cgit v1.2.3 From a0130d682222ae21afc395aead7cd2d87e1a8358 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 15:12:17 -0700 Subject: net: ibm: emac: Fix use-after-free during device removal The driver was using devm_register_netdev() which causes unregister_netdev() to be deferred until the devres cleanup phase, which runs after emac_remove() returns. This creates a use-after-free window where: 1. emac_remove() is called, which tears down hardware (cancels work, detaches modules, unregisters from MAL) 2. emac_remove() returns 3. devres cleanup runs and finally calls unregister_netdev() During step 3, the network stack might still process packets, triggering emac_irq(), emac_poll(), or other handlers that access now-freed hardware resources (dev->emacp, dev->mal, etc.). Fix this by replacing devm_register_netdev() with manual register_netdev() and calling unregister_netdev() at the beginning of emac_remove(), before any hardware teardown. This ensures the network device is fully stopped and unregistered before hardware resources are released. The change is safe because: - dev->ndev is assigned very early in probe (before any error paths that could bypass emac_remove) - platform_set_drvdata() is only called after successful registration, so emac_remove() only runs for fully registered devices - unregister_netdev() is idempotent and safe to call on any registered device Fixes: a4dd8535a527 ("net: ibm: emac: use devm for register_netdev") Signed-off-by: Rosen Penev Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/emac/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 417dfa18daae..4e503b3d0d2d 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3144,7 +3144,7 @@ static int emac_probe(struct platform_device *ofdev) netif_carrier_off(ndev); - err = devm_register_netdev(&ofdev->dev, ndev); + err = register_netdev(ndev); if (err) { printk(KERN_ERR "%pOF: failed to register net device (%d)!\n", np, err); @@ -3197,6 +3197,13 @@ static void emac_remove(struct platform_device *ofdev) DBG(dev, "remove" NL); + /* Unregister network device before tearing down hardware + * to prevent use-after-free during deferred cleanup. This ensures + * the network stack stops all operations before hardware resources + * are released. + */ + unregister_netdev(dev->ndev); + cancel_work_sync(&dev->reset_work); if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) -- cgit v1.2.3 From 48774e87bbaa0056819d4b52301e4692e50e3252 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:15 +0200 Subject: net: phy: clean the sfp upstream if phy probing fails Sashiko reported that we don't call sfp_bus_del_upstream() in the probe failure path, so let's add it, otherwise the sfp-bus is left with a dangling 'upstream' field, that may be used later on during SFP events. This issue existed before the generic phylib sfp support, back when drivers were calling phy_sfp_probe themselves. Reviewed-by: Nicolai Buchwitz Fixes: 298e54fa810e ("net: phy: add core phylib sfp support") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-2-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3370eb822017..6ccbfacf7d1d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1718,6 +1718,9 @@ static int phy_sfp_probe(struct phy_device *phydev) ret = sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); sfp_bus_put(bus); + + if (ret) + phydev->sfp_bus = NULL; } if (!ret && phydev->sfp_bus) @@ -3775,6 +3778,9 @@ static int phy_probe(struct device *dev) return 0; out: + sfp_bus_del_upstream(phydev->sfp_bus); + phydev->sfp_bus = NULL; + if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev); -- cgit v1.2.3 From b1e780bb37c641d8291c51d7b4bde33450d18fb4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:16 +0200 Subject: net: phy: remove phy ports upon probe failure When phy_probe fails, let's clean the phy_ports that were successfully added already. Suggested-by: Nicolai Buchwitz Reviewed-by: Nicolai Buchwitz Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-3-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 6ccbfacf7d1d..4ba880446896 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3781,6 +3781,8 @@ out: sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; + phy_cleanup_ports(phydev); + if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev); -- cgit v1.2.3 From 4497f5028675b7e51c4aa59c3f4df01f29424277 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:17 +0200 Subject: net: phy: Clean the phy_ports after unregistering the downstream SFP bus As reported by sashiko when looking a other patches, we need to ensure that the downstream SFP bus gets unregistered prior to destroying the phy_ports attached to a phy_device, as the SFP code may reference these ports. Let's make sure we follow that ordering in phy_remove(). Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation") Signed-off-by: Maxime Chevallier Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260604092819.723505-4-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 4ba880446896..b48722589f40 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3806,11 +3806,11 @@ static int phy_remove(struct device *dev) phydev->state = PHY_DOWN; - phy_cleanup_ports(phydev); - sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; + phy_cleanup_ports(phydev); + if (phydev->drv && phydev->drv->remove) phydev->drv->remove(phydev); -- cgit v1.2.3 From 5a0082ec20a05ef2378410323a5089a8f1786f4a Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Thu, 4 Jun 2026 11:28:18 +0200 Subject: net: phy: don't try to setup PHY-driven SFP cages when using genphy We don't have support for PHY-driver SFP cages with the genphy code. On top of that, it was found by sashiko that running sfp_bus_add_upstream() for genphy deadlocks, as for genphy the PHY probing runs under RTNL, which isn't the case for non-genphy drivers. This problem was reproduced, and does lead to a deadlock on RTNL. Before the blamed commit, the phy_sfp_probe() call was made by individual PHY drivers, so there was no way to get to the SFP probing path when using genphy. Let's therefore only run phy_sfp_probe when not using genphy. Reviewed-by: Nicolai Buchwitz Fixes: bad869b5e41a ("net: phy: Only rely on phy_port for PHY-driven SFP") Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260604092819.723505-5-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b48722589f40..1511385b9b36 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3512,9 +3512,15 @@ static int phy_setup_ports(struct phy_device *phydev) if (ret) return ret; - ret = phy_sfp_probe(phydev); - if (ret) - goto out; + /* We don't support SFP with genphy drivers. Also, genphy driver + * binding occurs with RTNL help, which will deadlock the call to + * sfp_bus_add_upstream(). + */ + if (!phydev->is_genphy_driven) { + ret = phy_sfp_probe(phydev); + if (ret) + goto out; + } if (phydev->n_ports < phydev->max_n_ports) { ret = phy_default_setup_single_port(phydev); -- cgit v1.2.3 From 894e036a24a26a6dd7b17d8d3fb5c53ab48a6074 Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 4 Jun 2026 16:58:49 +0300 Subject: net/mlx5: Fix slab-out-of-bounds in mlx5_query_nic_vport_mac_list mlx5_query_nic_vport_mac_list() sizes its firmware command buffer using the PF's log_max_current_uc/mc_list capabilities. When querying a VF vport with a larger configured max (via devlink), the firmware response can overflow this buffer: BUG: KASAN: slab-out-of-bounds in mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core] Read of size 4 at addr ff1100013ffc8a12 by task kworker/u96:2/385 CPU: 12 UID: 0 PID: 385 Comm: kworker/u96:2 Not tainted 7.0.0-rc6+ #1 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009) Workqueue: mlx5_esw_wq esw_vport_change_handler [mlx5_core] Call Trace: dump_stack_lvl+0x69/0xa0 print_report+0x176/0x4e4 kasan_report+0xc8/0x100 mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core] esw_update_vport_addr_list+0x2e3/0xda0 [mlx5_core] esw_vport_change_handle_locked+0xa1f/0x1060 [mlx5_core] esw_vport_change_handler+0x6a/0x90 [mlx5_core] process_one_work+0x87f/0x15e0 worker_thread+0x62b/0x1020 kthread+0x375/0x490 ret_from_fork+0x4dc/0x810 ret_from_fork_asm+0x11/0x20 Fix by querying the vport's own HCA caps to size the buffer correctly. Refactor the function to allocate and return the MAC list internally, removing the caller's dependency on knowing the correct max. Fixes: e16aea2744ab ("net/mlx5: Introduce access functions to modify/query vport mac lists") Signed-off-by: Dragos Tatulea Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260604135849.458060-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 13 +--- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 72 +++++++++++++++++------ include/linux/mlx5/vport.h | 4 +- 3 files changed, 59 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 7c8311f41232..236f89a6483a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -533,23 +533,16 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, struct mlx5_vport *vport, int list_type) { bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC; - u8 (*mac_list)[ETH_ALEN]; + u8 (*mac_list)[ETH_ALEN] = NULL; struct l2addr_node *node; struct vport_addr *addr; struct hlist_head *hash; struct hlist_node *tmp; - int size; + int size = 0; int err; int hi; int i; - size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) : - MLX5_MAX_MC_PER_VPORT(esw->dev); - - mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL); - if (!mac_list) - return; - hash = is_uc ? vport->uc_list : vport->mc_list; for_each_l2hash_node(node, tmp, hash, hi) { @@ -561,7 +554,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, goto out; err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type, - mac_list, &size); + &mac_list, &size); if (err) goto out; esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 4effe37fd455..d63b0e8806b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -324,35 +324,63 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu) } EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu); +static int mlx5_vport_max_mac_list_size(struct mlx5_core_dev *dev, u16 vport, + enum mlx5_list_type list_type) +{ + void *query_ctx, *hca_caps; + int ret = 0; + + if (!vport && !mlx5_core_is_ecpf(dev)) + return list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + + query_ctx = kzalloc(MLX5_ST_SZ_BYTES(query_hca_cap_out), GFP_KERNEL); + if (!query_ctx) + return -ENOMEM; + + ret = mlx5_vport_get_other_func_general_cap(dev, vport, query_ctx); + if (ret) + goto out; + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + ret = list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_uc_list) : + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_mc_list); + +out: + kfree(query_ctx); + + return ret; +} + int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u16 vport, enum mlx5_list_type list_type, - u8 addr_list[][ETH_ALEN], - int *list_size) + u8 (**addr_list)[ETH_ALEN], + int *addr_list_size) { u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {0}; + int allowed_list_size; void *nic_vport_ctx; int max_list_size; - int req_list_size; int out_sz; void *out; int err; int i; - req_list_size = *list_size; + if (!addr_list || !addr_list_size) + return -EINVAL; - max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ? - 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : - 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + *addr_list = NULL; + *addr_list_size = 0; - if (req_list_size > max_list_size) { - mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n", - req_list_size, max_list_size); - req_list_size = max_list_size; - } + max_list_size = mlx5_vport_max_mac_list_size(dev, vport, list_type); + if (max_list_size < 0) + return max_list_size; out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_out) + - req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + max_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); out = kvzalloc(out_sz, GFP_KERNEL); if (!out) @@ -371,16 +399,24 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out, nic_vport_context); - req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, - allowed_list_size); + allowed_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, + allowed_list_size); + if (!allowed_list_size) + goto out; + + *addr_list = kcalloc(allowed_list_size, ETH_ALEN, GFP_KERNEL); + if (!*addr_list) { + err = -ENOMEM; + goto out; + } - *list_size = req_list_size; - for (i = 0; i < req_list_size; i++) { + for (i = 0; i < allowed_list_size; i++) { u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx, current_uc_mac_address[i]) + 2; - ether_addr_copy(addr_list[i], mac_addr); + ether_addr_copy((*addr_list)[i], mac_addr); } + *addr_list_size = allowed_list_size; out: kvfree(out); return err; diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index dfa2fe32217a..282ed5442282 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -102,8 +102,8 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev, int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u16 vport, enum mlx5_list_type list_type, - u8 addr_list[][ETH_ALEN], - int *list_size); + u8 (**mac_list)[ETH_ALEN], + int *mac_list_size); int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, enum mlx5_list_type list_type, u8 addr_list[][ETH_ALEN], -- cgit v1.2.3 From b69004f5a6ad32da84d8aa5b23b9c0caafe6252e Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 4 Jun 2026 16:54:46 +0300 Subject: net/mlx5e: xsk: Fix DMA and xdp_frame leak on XDP_TX xmit failure In the XSK branch of mlx5e_xmit_xdp_buff(), when sq->xmit_xdp_frame() returns false (e.g. XDPSQ is full), the function returns without unmapping the DMA address or freeing the xdp_frame allocated by xdp_convert_zc_to_xdp_frame(). The xdpi_fifo push only happens on success, so the completion path cannot recover these entries. With CONFIG_DMA_API_DEBUG=y, the leak surfaces on driver unbind: DMA-API: pci 0000:08:00.0: device driver has pending DMA allocations while released from device [count=1116] One of leaked entries details: [device address=0x000000010ffd7028] [size=1534 bytes] [mapped with DMA_TO_DEVICE] [mapped as phy] WARNING: kernel/dma/debug.c:881 at dma_debug_device_change+0x127/0x180 ... DMA-API: Mapped at: debug_dma_map_phys+0x4b/0xd0 dma_map_phys+0xfd/0x2d0 mlx5e_xdp_handle+0x5ae/0xac0 [mlx5_core] mlx5e_xsk_skb_from_cqe_mpwrq_linear+0xc4/0x170 [mlx5_core] mlx5e_handle_rx_cqe_mpwrq+0xc1/0x290 [mlx5_core] Add the missing unmap + xdp_return_frame, matching the cleanup already done in mlx5e_xdp_xmit(). has_frags is rejected earlier in this branch, so no per-frag unmap is needed. Fixes: 84a0a2310d6d ("net/mlx5e: XDP_TX from UMEM support") Signed-off-by: Dragos Tatulea Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260604135446.456119-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index d3bab198c99c..d8c7cb8837d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -103,9 +103,15 @@ mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq, xdptxd->dma_addr = dma_addr; - if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, xdptxd, 0, NULL))) + if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, + mlx5e_xmit_xdp_frame_mpwqe, + mlx5e_xmit_xdp_frame, + sq, xdptxd, 0, NULL))) { + dma_unmap_single(sq->pdev, dma_addr, xdptxd->len, + DMA_TO_DEVICE); + xdp_return_frame(xdpf); return false; + } /* xmit_mode == MLX5E_XDP_XMIT_MODE_FRAME */ mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, -- cgit v1.2.3 From a7767290e77ca2e926b49f8bfa29daa12262c612 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Fri, 5 Jun 2026 18:21:12 +0800 Subject: net/mlx5: Use effective affinity mask for IRQ selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a sf is created after a CPU has been taken offline, the IRQ pool may contain IRQs with affinity masks that include the offline CPU. Since only online CPUs should be considered for IRQ placement, cpumask_subset() check would fail because the iter_mask contains offline CPUs that are not present in req_mask, causing sf creation to fail. This is an example: 1. When mlx5 driver loads, it initializes the IRQ pools. For sf_ctrl_pool with ≤64 sf: - xa_num_irqs = {N, N} (There is only one slot) 2. When the first SF is created: - The ctrl IRQ is allocated with mask=cpu_online_mask={0-191} 2. We take CPU 20 offline 3. Existing ctl irq still have mask={0-191} 4. Create a new SF: - req_mask={0-19,21-191} - iter_mask={0-191} - {0-191} is NOT a subset of {0-19,21-191} - least_loaded_irq=NULL 5. Try to allocate a new irq via irq_pool_request_irq() 6. xa_alloc() fails because the pool is full(There is only one slot) 7. sf creation fails with error Use irq_get_effective_affinity_mask() instead, which returns the IRQ's actual effective affinity that already excludes offline CPUs. Fixes: 061f5b23588a ("net/mlx5: SF, Use all available cpu for setting cpu affinity") Suggested-by: Shay Drory Signed-off-by: Fushuai Wang Reviewed-by: Shay Drory Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260605102112.91772-1-fushuai.wang@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c index 994fe83da4be..a0bb8ee44e35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c @@ -105,9 +105,12 @@ irq_pool_find_least_loaded(struct mlx5_irq_pool *pool, const struct cpumask *req lockdep_assert_held(&pool->lock); xa_for_each_range(&pool->irqs, index, iter, start, end) { - struct cpumask *iter_mask = mlx5_irq_get_affinity_mask(iter); int iter_refcount = mlx5_irq_read_locked(iter); + const struct cpumask *iter_mask; + iter_mask = irq_get_effective_affinity_mask(mlx5_irq_get_irq(iter)); + if (!iter_mask) + continue; if (!cpumask_subset(iter_mask, req_mask)) /* skip IRQs with a mask which is not subset of req_mask */ continue; -- cgit v1.2.3 From 19440600e729d4f74a42591a872099cf25c7d28a Mon Sep 17 00:00:00 2001 From: Chih Kai Hsu Date: Thu, 4 Jun 2026 17:22:47 +0800 Subject: r8152: handle the return value of usb_reset_device() If usb_reset_device() returns a negative error code, stop the process of probing. Fixes: 10c3271712f5 ("r8152: disable the ECM mode") Signed-off-by: Chih Kai Hsu Reviewed-by: Hayes Wang Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260604092247.27158-450-nic_swsd@realtek.com Signed-off-by: Paolo Abeni --- drivers/net/usb/r8152.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 1ace1d2398c9..b1268553cd70 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -9851,7 +9851,12 @@ static int rtl8152_probe_once(struct usb_interface *intf, struct net_device *netdev; int ret; - usb_reset_device(udev); + ret = usb_reset_device(udev); + if (ret < 0) { + dev_err(&intf->dev, "USB reset failed, errno=%d\n", ret); + return ret; + } + netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); -- cgit v1.2.3 From 09a5bf856aa759513afc4afd233d15bcc711b84e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Thu, 4 Jun 2026 22:37:56 +0800 Subject: octeontx2-af: fix memory leak in rvu_setup_hw_resources() If rvu_npc_exact_init() fails in rvu_setup_hw_resources(), the function returns directly instead of jumping to the error handling path. This causes a resource leak for the previously initialized CGX, NPC, fwdata, and MSI-X states. Fix this by replacing the direct return with goto cgx_err to ensure proper cleanup. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6. An x86_64 allyesconfig build showed no new warnings. As we do not have access to Marvell OcteonTX2 RVU AF hardware to test with, no runtime testing was able to be performed. Fixes: 3571fe07a090 ("octeontx2-af: Drop rules for NPC MCAM") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Signed-off-by: Zilin Guan Link: https://patch.msgid.link/20260604143756.1524482-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 3cf131508ecf..6e907ee19164 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1160,7 +1160,7 @@ cpt: err = rvu_npc_exact_init(rvu); if (err) { dev_err(rvu->dev, "failed to initialize exact match table\n"); - return err; + goto cgx_err; } /* Assign MACs for CGX mapped functions */ -- cgit v1.2.3 From 004e9ecfe6c5384f9e0b2f6f6389d42ec22789af Mon Sep 17 00:00:00 2001 From: Anton Leontev Date: Thu, 4 Jun 2026 19:59:38 +0300 Subject: hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf netvsc_copy_to_send_buf() copies page buffer entries into the VMBus send buffer using phys_to_virt() on the entry PFN. Entries for the RNDIS header and the skb linear data come from kmalloc'd memory and are always in the kernel direct map, but entries for skb fragments reference page cache or user pages, which on 32-bit x86 with CONFIG_HIGHMEM=y can live above the LOWMEM boundary. For such a page phys_to_virt() returns an address outside the direct map and the subsequent memcpy() faults on the transmit softirq path, which is fatal. Map the pages with kmap_local_page() instead, handling two properties of the page buffer entries: - pb[i].pfn is a Hyper-V PFN at HV_HYP_PAGE_SIZE (4K) granularity, not a native PFN. Reconstruct the physical address first and derive the native page from it, so the mapping stays correct where PAGE_SIZE > HV_HYP_PAGE_SIZE (e.g. arm64 with 64K pages). - Since commit 41a6328b2c55 ("hv_netvsc: Preserve contiguous PFN grouping in the page buffer array"), an entry describes a full physically contiguous fragment and pb[i].len can exceed PAGE_SIZE, while kmap_local_page() maps a single page. Copy page by page, splitting at native page boundaries. The copy path only handles packets smaller than the send section size (6144 bytes by default); larger packets take the cp_partial path where only the RNDIS header is copied. So entries here are bounded by the section size and a copy is split at most once on 4K-page systems. On !CONFIG_HIGHMEM configs kmap_local_page() folds to page_address() and no mapping work is added. Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path") Cc: stable@vger.kernel.org Signed-off-by: Anton Leontev Link: https://patch.msgid.link/20260604165938.32033-1-leontyevantony@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/hyperv/netvsc.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 59e95341f9b1..4d319c50955e 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -965,12 +966,22 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device, } for (i = 0; i < page_count; i++) { - char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT); - u32 offset = pb[i].offset; + phys_addr_t paddr = (pb[i].pfn << HV_HYP_PAGE_SHIFT) + + pb[i].offset; u32 len = pb[i].len; - memcpy(dest, (src + offset), len); - dest += len; + while (len) { + struct page *page = phys_to_page(paddr); + u32 off = offset_in_page(paddr); + u32 chunk = min_t(u32, len, PAGE_SIZE - off); + char *src = kmap_local_page(page); + + memcpy(dest, src + off, chunk); + kunmap_local(src); + dest += chunk; + paddr += chunk; + len -= chunk; + } } if (padding) -- cgit v1.2.3 From 6edb934de9bda3b7abcec856eaee6fc8b4278dd1 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 15:33:13 +0800 Subject: gpio: zynq: fix runtime PM leak on remove pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error. zynq_gpio_remove() uses it to keep the controller active while removing the GPIO chip, but never drops the usage counter again. Balance the get with pm_runtime_put_noidle() after disabling runtime PM. Fixes: 3242ba117e9b ("gpio: Add driver for Zynq GPIO controller") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260609073313.5-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 571e366624d2..fafca91128b2 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -1014,6 +1014,7 @@ static void zynq_gpio_remove(struct platform_device *pdev) gpiochip_remove(&gpio->chip); device_set_wakeup_capable(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } static struct platform_driver zynq_gpio_driver = { -- cgit v1.2.3 From 446e8c31d0fc7f1d92c06c2d2f7e7ed27f55f0c6 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 00:45:38 +0000 Subject: gpio: mockup: reject invalid gpio_mockup_ranges widths gpio-mockup validates only that each second gpio_mockup_ranges value is non-negative before creating the mock chips. The fixed-base form uses the second value as the first GPIO number after the range, while the dynamic-base form uses it as the number of GPIOs. gpio_mockup_register_chip() stores the resulting number of GPIOs in a u16 and passes it through a PROPERTY_ENTRY_U16("nr-gpios", ...). Values greater than U16_MAX therefore truncate silently. For example, gpio_mockup_ranges=-1,65537 creates a one-line mock GPIO chip instead of rejecting the invalid request. Reject zero-width, reversed, and over-U16 ranges before registering any mock chip. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://patch.msgid.link/20260609004538.1240091.3fba33a20b88.gpio-mockup-ngpio-u16-truncation@trailofbits.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index a7d69f3835c1..91ff789c4fa6 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -578,7 +579,7 @@ static int __init gpio_mockup_register_chip(int idx) static int __init gpio_mockup_init(void) { - int i, num_chips, err; + int i, num_chips, err, base, ngpio; if ((gpio_mockup_num_ranges % 2) || (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) @@ -592,8 +593,19 @@ static int __init gpio_mockup_init(void) * always be greater than 0. */ for (i = 0; i < num_chips; i++) { - if (gpio_mockup_range_ngpio(i) < 0) + base = gpio_mockup_range_base(i); + ngpio = gpio_mockup_range_ngpio(i); + + if (ngpio <= 0) return -EINVAL; + + if (base < 0) { + if (ngpio > U16_MAX) + return -EINVAL; + } else { + if (ngpio <= base || ngpio - base > U16_MAX) + return -EINVAL; + } } gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL); -- cgit v1.2.3 From 1c1e0fc88d6ef65bf15d517853251f75ab9d18c3 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Mon, 8 Jun 2026 01:05:02 +0200 Subject: gpio: rockchip: fix generic IRQ chip leak on remove The driver allocates domain generic chips using irq_alloc_domain_generic_chips() during probe. However, on driver remove/teardown, the generic chips are not automatically freed when the IRQ domain is removed because the domain flags do not include IRQ_DOMAIN_FLAG_DESTROY_GC. This causes both the domain generic chips structure and the associated generic chips to be leaked. Additionally, the generic chips remain on the global gc_list and may later be visited by generic IRQ chip suspend, resume, or shutdown callbacks after the GPIO bank has been removed, potentially resulting in a use-after-free and kernel crash. Fix the resource leak by explicitly calling irq_domain_remove_generic_chips() before removing the IRQ domain in rockchip_gpio_remove(). Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260607230504.35392-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index bc97d5d5d329..9478a58f1caa 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -802,8 +802,10 @@ static void rockchip_gpio_remove(struct platform_device *pdev) struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); irq_set_chained_handler_and_data(bank->irq, NULL, NULL); - if (bank->domain) + if (bank->domain) { + irq_domain_remove_generic_chips(bank->domain); irq_domain_remove(bank->domain); + } gpiochip_remove(&bank->gpio_chip); } -- cgit v1.2.3 From 54665dce982689e2fd99b32e9a0dcc204fda8a51 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 8 Jun 2026 09:25:40 +0800 Subject: net: mctp: usb: fix race between urb completion and rx_retry cancellation It's possible that sequencing between setting ->stopped and cancelling the rx_retry work (in ndo_stop) could leave us with an urb queued: T1: ndo_stop T2: rx_retry_work ------------ ---------------- LD: ->stopped => false ST: ->stopped <= true usb_kill_urb() mctp_usb_rx_queue() usb_submit_urb() cancel_delayed_work_sync() That urb completion can then re-schedule rx_retry_work. Strenghen the sequencing between the stop (preventing another requeue) and the cancel by updating both atomically under a new rx lock. After setting ->rx_stopped, and cancelling pending work, we know that the requeue cannot occur, so all that's left is killing any pending urb. Fixes: 0791c0327a6e ("net: mctp: Add MCTP USB transport driver") Signed-off-by: Jeremy Kerr Link: https://patch.msgid.link/20260608-dev-mctp-usb-rx-requeue-v2-1-29a3aa507609@codeconstruct.com.au Signed-off-by: Paolo Abeni --- drivers/net/mctp/mctp-usb.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c index 3b5dff144177..cf6f6a93a451 100644 --- a/drivers/net/mctp/mctp-usb.c +++ b/drivers/net/mctp/mctp-usb.c @@ -22,7 +22,6 @@ struct mctp_usb { struct usb_device *usbdev; struct usb_interface *intf; - bool stopped; struct net_device *netdev; @@ -32,6 +31,9 @@ struct mctp_usb { struct urb *tx_urb; struct urb *rx_urb; + /* enforces atomic access to rx_stopped and requeuing the retry work */ + spinlock_t rx_lock; + bool rx_stopped; struct delayed_work rx_retry_work; }; @@ -122,6 +124,7 @@ static const unsigned long RX_RETRY_DELAY = HZ / 4; static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) { + unsigned long flags; struct sk_buff *skb; int rc; @@ -147,7 +150,10 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) return rc; err_retry: - schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + if (!mctp_usb->rx_stopped) + schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); return rc; } @@ -248,9 +254,6 @@ static void mctp_usb_rx_retry_work(struct work_struct *work) struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, rx_retry_work.work); - if (READ_ONCE(mctp_usb->stopped)) - return; - mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); } @@ -258,7 +261,7 @@ static int mctp_usb_open(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); - WRITE_ONCE(mctp_usb->stopped, false); + WRITE_ONCE(mctp_usb->rx_stopped, false); netif_start_queue(dev); @@ -268,17 +271,21 @@ static int mctp_usb_open(struct net_device *dev) static int mctp_usb_stop(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); + unsigned long flags; netif_stop_queue(dev); /* prevent RX submission retry */ - WRITE_ONCE(mctp_usb->stopped, true); + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + mctp_usb->rx_stopped = true; + cancel_delayed_work(&mctp_usb->rx_retry_work); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); + + flush_delayed_work(&mctp_usb->rx_retry_work); usb_kill_urb(mctp_usb->rx_urb); usb_kill_urb(mctp_usb->tx_urb); - cancel_delayed_work_sync(&mctp_usb->rx_retry_work); - return 0; } @@ -331,6 +338,7 @@ static int mctp_usb_probe(struct usb_interface *intf, dev->netdev = netdev; dev->usbdev = interface_to_usbdev(intf); dev->intf = intf; + spin_lock_init(&dev->rx_lock); usb_set_intfdata(intf, dev); dev->ep_in = ep_in->bEndpointAddress; -- cgit v1.2.3 From 881a3113b74964918cdd72747e3bc119c02b0c0c Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 8 Jun 2026 09:25:41 +0800 Subject: net: mctp: usb: don't fail mctp_usb_rx_queue on a deferred submission In the ndo_open path, a deferred queue open will report a failure, and so the netdev will not be ndo_stop()ed, leaving us with the rx_retry work potentially pending. Don't report a deferred queue as an error, as we are still operational. This means we use the ndo_stop() path for future cleanup, which handles rx_retry_work cancellation. Fixes: 0791c0327a6e ("net: mctp: Add MCTP USB transport driver") Signed-off-by: Jeremy Kerr Link: https://patch.msgid.link/20260608-dev-mctp-usb-rx-requeue-v2-2-29a3aa507609@codeconstruct.com.au Signed-off-by: Paolo Abeni --- drivers/net/mctp/mctp-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c index cf6f6a93a451..fade65f2f269 100644 --- a/drivers/net/mctp/mctp-usb.c +++ b/drivers/net/mctp/mctp-usb.c @@ -154,7 +154,7 @@ err_retry: if (!mctp_usb->rx_stopped) schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); - return rc; + return 0; } static void mctp_usb_in_complete(struct urb *urb) -- cgit v1.2.3 From d930276f2cddd0b7294cac7a8fe7b877f6d9e08d Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Fri, 5 Jun 2026 17:25:24 -0500 Subject: bnxt_en: Fix NULL pointer dereference PCIe errors detected by a Root Port or Downstream Port cause error recovery services to run on all subordinate devices regardless of administrative state. The .error_detected() callback, bnxt_io_error_detected(), disables and synchronizes IRQs via bnxt_disable_int_sync(), which calls bnxt_cp_num_to_irq_num() to map completion rings to IRQs using bp->bnapi. Since bp->bnapi is allocated on NIC open and freed on NIC close, PCIe error recovery on a closed NIC can dereference a NULL pointer. Check if bp->bnapi is NULL before disabling and synchronizing IRQs. Fixes: e5811b8c09df ("bnxt_en: Add IRQ remapping logic.") Cc: stable@vger.kernel.org Signed-off-by: Kyle Meyer Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/aiNM1CY2-StPilxW@hpe.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 35e1f8f663c7..c999f9733326 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5748,7 +5748,7 @@ static void bnxt_disable_int_sync(struct bnxt *bp) { int i; - if (!bp->irq_tbl) + if (!bp->irq_tbl || !bp->bnapi) return; atomic_inc(&bp->intr_sem); -- cgit v1.2.3 From 7f2fcff15e99bb852f6967396ed12b38376e2c8d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 6 Jun 2026 22:44:28 -0700 Subject: tun: zero the whole vnet header in tun_put_user() tun_put_user() declares an on-stack struct virtio_net_hdr_v1_hash_tunnel without zeroing it. For a non-tunnel skb, virtio_net_hdr_tnl_from_skb() only initializes the first 10 bytes (sizeof(struct virtio_net_hdr)), leaving bytes 10..23 (num_buffers and the hash/tunnel fields) as stack garbage. An unprivileged user can set the vnet header size to 24 with TUNSETVNETHDRSZ, so __tun_vnet_hdr_put() copies all 24 bytes of the partially-initialized struct to userspace, leaking 14 bytes of kernel stack on every read of a non-tunnel packet. Fix it the same way tun_get_user() already does by zeroing the whole header right after declaration. Fixes: 288f30435132 ("tun: enable gso over UDP tunnel support.") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260607054428.3050243-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9e7744eb57a3..fed9dfdfcc3b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2070,6 +2070,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, struct virtio_net_hdr_v1_hash_tunnel hdr; struct virtio_net_hdr *gso; + memset(&hdr, 0, sizeof(hdr)); ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb, &hdr); if (ret) -- cgit v1.2.3 From 627366c51145a07f675b1800fb5ea2ec960bd900 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Mon, 8 Jun 2026 15:59:52 +0000 Subject: ptp: ocp: fix resource freeing order Commit a60fc3294a37 ("ptp: rework ptp_clock_unregister() to disable events") added a call to ptp_disable_all_events() which changes the configuration of pins if they support EXTTS events. In ptp_ocp_detach() pins resources are freed before ptp_clock_unregister() and it leads to use-after-free during driver removal. Fix it by changing the order of free/unregister calls. To avoid irq handler running on the other core while ptp device unregistering, call synchronize_irq() after HW is configured to stop producing irqs and no irqs are in-flight. Fixes: a60fc3294a37 ("ptp: rework ptp_clock_unregister() to disable events") Signed-off-by: Vadim Fedorenko Link: https://patch.msgid.link/20260608155952.240304-1-vadim.fedorenko@linux.dev Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ocp.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index beacc2ffb166..735385539b9f 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -2479,8 +2479,13 @@ ptp_ocp_ts_enable(void *priv, u32 req, bool enable) iowrite32(1, ®->intr_mask); iowrite32(1, ®->intr); } else { + int irq_vec = pci_irq_vector(bp->pdev, ext->irq_vec); + iowrite32(0, ®->intr_mask); iowrite32(0, ®->enable); + ioread32(®->intr_mask); + if (irq_vec > 0) + synchronize_irq(irq_vec); } return 0; @@ -4867,6 +4872,22 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_detach_sysfs(bp); ptp_ocp_attr_group_del(bp); timer_delete_sync(&bp->watchdog); + /* Disable interrupts on all timestampers */ + if (bp->ts0) + ptp_ocp_ts_enable(bp->ts0, 0, false); + if (bp->ts1) + ptp_ocp_ts_enable(bp->ts1, 0, false); + if (bp->ts2) + ptp_ocp_ts_enable(bp->ts2, 0, false); + if (bp->ts3) + ptp_ocp_ts_enable(bp->ts3, 0, false); + if (bp->ts4) + ptp_ocp_ts_enable(bp->ts4, 0, false); + if (bp->pps) + ptp_ocp_ts_enable(bp->pps, ~0, false); + if (bp->ptp) + ptp_clock_unregister(bp->ptp); + kfree(bp->ptp_info.pin_config); ptp_ocp_unregister_ext(bp->ts0); ptp_ocp_unregister_ext(bp->ts1); ptp_ocp_unregister_ext(bp->ts2); @@ -4884,9 +4905,6 @@ ptp_ocp_detach(struct ptp_ocp *bp) clk_hw_unregister_fixed_rate(bp->i2c_clk); if (bp->n_irqs) pci_free_irq_vectors(bp->pdev); - if (bp->ptp) - ptp_clock_unregister(bp->ptp); - kfree(bp->ptp_info.pin_config); device_unregister(&bp->dev); } -- cgit v1.2.3 From 64911f5aac534191e6b9a52ca1d50ba870a12d86 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 9 Jun 2026 14:17:50 +0200 Subject: gpio: fix cleanup path on hog failure If gpiochip_hog_lines() successfully processes some hogs but fails on a later one, the error handling path in gpiochip_add_data_with_key() jumps directly to err_remove_of_chip. This leaks resources allocated earlier for ACPI, interrupts and hogs that were successfully processed. Use the right label in error path. Closes: https://sashiko.dev/#/patchset/20260608210108.36248-1-dan%40reactivated.net Fixes: d1d564ec4992 ("gpio: move hogs into GPIO core") Reviewed-by: Andy Shevchenko Reviewed-by: Mika Westerberg Link: https://patch.msgid.link/20260609-gpio-hogs-fixes-v1-2-b4064f8070e7@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e6dce430dca..d3e0d77f74e7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1291,7 +1291,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiochip_hog_lines(gc); if (ret) - goto err_remove_of_chip; + goto err_free_hogs; ret = gpiochip_irqchip_init_valid_mask(gc); if (ret) -- cgit v1.2.3 From a23226b7c1f69eafd9ced4e037fb51c9758c0501 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 8 Jun 2026 22:01:08 +0100 Subject: gpiolib: handle gpio-hogs only once Commit d1d564ec49929 ("gpio: move hogs into GPIO core") introduced a behaviour change that breaks boot on Raspberry Pi 5 when using the firmware-supplied device tree: gpiochip_add_data_with_key: GPIOs 544..575 (/soc@107c000000/gpio@7d517c00) failed to register, -22 brcmstb-gpio 107d517c00.gpio: Could not add gpiochip for bank 1 brcmstb-gpio 107d517c00.gpio: probe with driver brcmstb-gpio failed with error -22 gpio-brcmstb registers two gpio_chips against the device tree node gpio@7d517c00, one for each bank. The firmware-supplied DT includes a gpio-hog on RP1 RUN, and this gpio-hog is attempted to be applied to *both* gpio_chips. This succeeds against bank 0 (which hosts the GPIO) and fails for bank 1 (which does not). In the previous implementation, failures to apply gpio-hogs were quietly ignored. In the new code, the error code propagates and causes probe to fail. Closely approximate the previous behaviour by using the OF_POPULATED flag to ensure that each gpio-hog is processed only once. The flag was previously being set before the gpio-hogs were processed, so as part of this change, the flag now gets set only after the gpio-hog is actioned. The handling of gpio-hogs on a DT node with multiple gpio_chips remains a bit incomplete/unclear, but this at least retains the ability to apply hogs to the first gpio_chip per node. Fixes: d1d564ec49929 ("gpio: move hogs into GPIO core") Signed-off-by: Daniel Drake Link: https://patch.msgid.link/20260608210108.36248-1-dan@reactivated.net Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 5 ----- drivers/gpio/gpiolib.c | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2c923d17541f..813dbcb91f6f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1066,11 +1066,6 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(np); - for_each_available_child_of_node_scoped(np, child) { - if (of_property_read_bool(child, "gpio-hog")) - of_node_set_flag(child, OF_POPULATED); - } - return ret; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d3e0d77f74e7..c1f9c0d367d5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1031,9 +1031,17 @@ static int gpiochip_hog_lines(struct gpio_chip *gc) if (!fwnode_property_present(fwnode, "gpio-hog")) continue; + /* The hog may have been handled by another gpio_chip on the same fwnode */ + if (is_of_node(fwnode) && + of_node_check_flag(to_of_node(fwnode), OF_POPULATED)) + continue; + ret = gpiochip_add_hog(gc, fwnode); if (ret) return ret; + + if (is_of_node(fwnode)) + of_node_set_flag(to_of_node(fwnode), OF_POPULATED); } return 0; -- cgit v1.2.3 From 180235600934bef6add3be637c296d6cf3272e67 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:40 +0200 Subject: net: mvpp2: sync RX data at the hardware packet offset mvpp2 programs the RX queue packet offset, so hardware writes received data at dma_addr + MVPP2_SKB_HEADROOM. The current CPU sync starts at dma_addr and only covers rx_bytes + MVPP2_MH_SIZE bytes, which syncs the unused headroom and misses the same number of bytes at the packet tail. On non-coherent DMA systems this can leave the CPU reading stale cache contents for the end of the received frame. Use dma_sync_single_range_for_cpu() with MVPP2_SKB_HEADROOM as the range offset so the sync covers the Marvell header and packet data actually written by hardware. Fixes: e1921168bbd4 ("mvpp2: sync only the received frame") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-2-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index f442b874bb59..92a701f4fe3f 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3946,9 +3946,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_dir = DMA_FROM_DEVICE; } - dma_sync_single_for_cpu(dev->dev.parent, dma_addr, - rx_bytes + MVPP2_MH_SIZE, - dma_dir); + dma_sync_single_range_for_cpu(dev->dev.parent, dma_addr, + MVPP2_SKB_HEADROOM, + rx_bytes + MVPP2_MH_SIZE, + dma_dir); /* Buffer header not supported */ if (rx_status & MVPP2_RXD_BUF_HDR) -- cgit v1.2.3 From f3c6aa078927e6fe8121c9c591ddee8716c5305a Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:41 +0200 Subject: net: mvpp2: limit XDP frame size to the RX buffer mvpp2 has short and long BM pools, and short pool buffers can be smaller than PAGE_SIZE. The XDP path nevertheless initializes every xdp_buff with PAGE_SIZE as frame size. XDP helpers use frame_sz to validate tail growth and to derive the hard end of the data area. Advertising PAGE_SIZE for short buffers can let bpf_xdp_adjust_tail() grow a packet past the real allocation, corrupting memory or later tripping skb tailroom checks. Initialize the XDP buffer with bm_pool->frag_size so XDP tailroom matches the actual buffer backing the packet. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-3-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 92a701f4fe3f..3372ed27cc8d 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3979,7 +3979,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else xdp_rxq = &rxq->xdp_rxq_long; - xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq); + xdp_init_buff(&xdp, bm_pool->frag_size, xdp_rxq); xdp_prepare_buff(&xdp, data, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM, rx_bytes, true); -- cgit v1.2.3 From 5e8e2a9624df72fca7c736b2966b2cbf6c9c3ff6 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:42 +0200 Subject: net: mvpp2: refill RX buffers before XDP or skb use The RX error path returns the current descriptor buffer to the hardware BM pool. That is only valid while the driver still owns the buffer. mvpp2_rx_refill() can fail after the current buffer has been handed to XDP or attached to an skb. In those cases mvpp2_run_xdp() may have recycled, redirected, or queued the page for XDP_TX, and an skb free also retires the data buffer. Returning such a buffer to BM lets hardware DMA into memory that is no longer owned by the RX ring. Refill the BM pool before handing the current buffer to XDP or to the skb. If the allocation fails there, drop the packet and return the still-owned current buffer to BM, preserving the pool depth. Once the refill succeeds, later local drops retire/free the current buffer instead of returning it to BM. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Fixes: d6526926de73 ("net: mvpp2: fix memory leak in mvpp2_rx") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-4-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 43 ++++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 3372ed27cc8d..481daafdc1cb 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3971,6 +3971,12 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else frag_size = bm_pool->frag_size; + err = mvpp2_rx_refill(port, bm_pool, pp, pool); + if (err) { + netdev_err(port->dev, "failed to refill BM pools\n"); + goto err_drop_frame; + } + if (xdp_prog) { struct xdp_rxq_info *xdp_rxq; @@ -3988,12 +3994,6 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, if (ret) { xdp_ret |= ret; - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - goto err_drop_frame; - } - ps.rx_packets++; ps.rx_bytes += rx_bytes; continue; @@ -4008,8 +4008,21 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb = slab_build_skb(data); if (!skb) { netdev_warn(port->dev, "skb build failed\n"); - goto err_drop_frame; + if (pp) { + page_pool_put_page(pp, virt_to_head_page(data), + rx_bytes + MVPP2_MH_SIZE, + true); + } else { + dma_unmap_single_attrs(dev->dev.parent, dma_addr, + bm_pool->buf_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + mvpp2_frag_free(bm_pool, pp, data); + } + goto err_drop_frame_retired; } + if (pp) + skb_mark_for_recycle(skb); /* If we have RX hardware timestamping enabled, grab the * timestamp from the queue and convert. @@ -4020,16 +4033,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb_hwtstamps(skb)); } - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - dev_kfree_skb_any(skb); - goto err_drop_frame; - } - - if (pp) - skb_mark_for_recycle(skb); - else + if (!pp) dma_unmap_single_attrs(dev->dev.parent, dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); @@ -4048,13 +4052,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, continue; err_drop_frame: - dev->stats.rx_errors++; - mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ if (rx_status & MVPP2_RXD_BUF_HDR) mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status); else mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); +err_drop_frame_retired: + dev->stats.rx_errors++; + mvpp2_rx_error(port, rx_desc); } if (xdp_ret & MVPP2_XDP_REDIR) -- cgit v1.2.3 From 77a6b90ce56bc982dcfa94229b8e28e6abb16e95 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Sun, 7 Jun 2026 15:49:43 +0200 Subject: net: mvpp2: build skb from XDP-adjusted data on XDP_PASS When an XDP program uses bpf_xdp_adjust_head() or bpf_xdp_adjust_tail() and then returns XDP_PASS, mvpp2 still builds the skb from fixed offsets derived from the original RX descriptor. Packet geometry changes made by the XDP program are therefore discarded before the skb reaches the stack. Update rx_offset and rx_bytes from xdp.data and xdp.data_end for XDP_PASS. This makes skb_reserve() and skb_put() reflect the packet seen by XDP, and makes RX byte accounting for XDP_PASS follow the length of the skb passed to the network stack. Keep a separate rx_sync_size for page-pool recycling on skb allocation failure, which must stay tied to the received buffer range. Non-PASS verdicts continue to account the descriptor length because no skb is passed up in those cases. Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support") Signed-off-by: Til Kaiser Link: https://patch.msgid.link/20260607134943.21996-5-mail@tk154.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 481daafdc1cb..ccc24a1301f2 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3917,10 +3917,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, struct mvpp2_bm_pool *bm_pool; struct page_pool *pp = NULL; struct sk_buff *skb; - unsigned int frag_size; + unsigned int frag_size, rx_sync_size; dma_addr_t dma_addr; phys_addr_t phys_addr; - int pool, rx_bytes, err, ret; + int pool, rx_bytes, rx_offset, err, ret; struct page *page; void *data; @@ -3933,6 +3933,8 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, rx_status = mvpp2_rxdesc_status_get(port, rx_desc); rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc); rx_bytes -= MVPP2_MH_SIZE; + rx_sync_size = rx_bytes + MVPP2_MH_SIZE; + rx_offset = MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM; dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc); pool = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> @@ -3948,7 +3950,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_sync_single_range_for_cpu(dev->dev.parent, dma_addr, MVPP2_SKB_HEADROOM, - rx_bytes + MVPP2_MH_SIZE, + rx_sync_size, dma_dir); /* Buffer header not supported */ @@ -3999,6 +4001,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, continue; } + rx_sync_size = max_t(unsigned int, rx_sync_size, + xdp.data_end - xdp.data_hard_start - + MVPP2_SKB_HEADROOM); + + /* Update offset and length to reflect any XDP adjustments. */ + rx_offset = xdp.data - data; + rx_bytes = xdp.data_end - xdp.data; + metasize = xdp.data - xdp.data_meta; } @@ -4010,8 +4020,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, netdev_warn(port->dev, "skb build failed\n"); if (pp) { page_pool_put_page(pp, virt_to_head_page(data), - rx_bytes + MVPP2_MH_SIZE, - true); + rx_sync_size, true); } else { dma_unmap_single_attrs(dev->dev.parent, dma_addr, bm_pool->buf_size, @@ -4041,7 +4050,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, ps.rx_packets++; ps.rx_bytes += rx_bytes; - skb_reserve(skb, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM); + skb_reserve(skb, rx_offset); skb_put(skb, rx_bytes); if (metasize) skb_metadata_set(skb, metasize); -- cgit v1.2.3 From 0487cfca46517ff6699c72dc1a8872b0af3c31a9 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:40 +0800 Subject: net: txgbe: initialize module info buffer The module info buffer should be initialized to 0 before the firmware returns information. Otherwise, there is a risk that the buffer field not filled by the firmware is random value. Fixes: 343929799ace ("net: txgbe: Support to handle GPIO IRQs for AML devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-2-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index f0514251d4f3..a7e81f9e1be1 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -335,7 +335,7 @@ static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) int txgbe_identify_module(struct wx *wx) { - struct txgbe_hic_get_module_info buffer; + struct txgbe_hic_get_module_info buffer = { 0 }; struct txgbe_sff_id *id; int err = 0; u32 mod_abs; -- cgit v1.2.3 From f2df54ddbfb04a006ee326a5d8270434a414e0af Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:41 +0800 Subject: net: txgbe: distinguish module types by checking identifier Rework txgbe_identify_module() to validate module identifiers through explicit type checks instead of relying on transceiver_type heuristics. When using the SFP module, transceiver_type could be a random value, because it was read from an invalid register. Fixes: 57d39faed4c9 ("net: txgbe: improve functions of AML 40G devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-3-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index a7e81f9e1be1..bdac654a2364 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -357,18 +357,16 @@ int txgbe_identify_module(struct wx *wx) } id = &buffer.id; - if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) { - wx_err(wx, "Invalid module\n"); - return -ENODEV; - } - - if (id->transceiver_type == 0xFF) + if (id->identifier == TXGBE_SFF_IDENTIFIER_SFP) return txgbe_sfp_to_linkmodes(wx, id); - return txgbe_qsfp_to_linkmodes(wx, id); + if (id->identifier == TXGBE_SFF_IDENTIFIER_QSFP || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP_PLUS || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP28) + return txgbe_qsfp_to_linkmodes(wx, id); + + wx_err(wx, "Invalid module\n"); + return -EINVAL; } void txgbe_setup_link(struct wx *wx) -- cgit v1.2.3 From 47f848aac4e79bdb197f849fa86e71fff1ad36ef Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 8 Jun 2026 15:08:42 +0800 Subject: net: txgbe: initialize PHY interface to 0 DECLARE_PHY_INTERFACE_MASK() does not guarantee zeroed contents. Add a new macro DECLARE_PHY_INTERFACE_MASK_ZERO(), make the stack variable to be zeroed before setting supported interfaces. Fixes: 57d39faed4c9 ("net: txgbe: improve functions of AML 40G devices") Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260608070842.36504-4-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 4 ++-- drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index bdac654a2364..8fc32df8e49a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -204,7 +204,7 @@ int txgbe_set_phy_link(struct wx *wx) static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) { @@ -271,7 +271,7 @@ static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_CR4) { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 6b05f32b4a01..877234e3fdc2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -315,6 +315,9 @@ void txgbe_up(struct wx *wx); int txgbe_setup_tc(struct net_device *dev, u8 tc); void txgbe_do_reset(struct net_device *netdev); +#define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \ + unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, } + #define TXGBE_LINK_SPEED_UNKNOWN 0 #define TXGBE_LINK_SPEED_10GB_FULL 4 #define TXGBE_LINK_SPEED_25GB_FULL 0x10 -- cgit v1.2.3 From 7360b96099806396f4ce15233f6dddcb69248d34 Mon Sep 17 00:00:00 2001 From: Kiran Kumar K Date: Mon, 8 Jun 2026 15:24:55 +0530 Subject: octeontx2-af: fix IP fragment flag corruption on custom KPU profile load npc_cn20k_apply_custom_kpu() overwrites KPU profile entries with custom firmware values and then calls npc_cn20k_update_action_entries_n_flags() over all entries. Since the same function already ran during default profile initialisation, entries not overridden by the custom firmware get their flags translated twice, corrupting the CN20K-specific values. Fix this by extracting the per-entry translation into a helper npc_cn20k_translate_action_flags() and calling it as each custom entry is loaded, removing the redundant batch call at the end. Fixes: ef992a0f12e8 ("octeontx2-af: npc: cn20k: MKEX profile support") Cc: Suman Ghosh Signed-off-by: Kiran Kumar K Signed-off-by: Nitin Shetty J Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260608095455.1499203-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/af/cn20k/npc.c | 72 ++++++++++------------ 1 file changed, 34 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 6b3f453fd500..fe8c4ffcd8f7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -1571,53 +1571,49 @@ static u8 npc_map2cn20k_flag(u8 flag) return 0xff; } +static void npc_cn20k_translate_action_flags(struct npc_kpu_profile_action *act) +{ + u8 ltype, val; + + if (act->lid != NPC_LID_LC) + return; + + ltype = act->ltype; + if (ltype != NPC_LT_LC_IP && + ltype != NPC_LT_LC_IP6 && + ltype != NPC_LT_LC_IP_OPT && + ltype != NPC_LT_LC_IP6_EXT) + return; + + switch (act->flags) { + case NPC_F_LC_U_IP_FRAG: + case NPC_F_LC_U_IP6_FRAG: + case NPC_F_LC_L_6TO4: + case NPC_F_LC_L_MPLS_IN_IP: + case NPC_F_LC_L_IP6_TUN_IP6: + case NPC_F_LC_L_IP6_MPLS_IN_IP: + val = npc_map2cn20k_flag(act->flags); + if (val != 0xFF) + act->flags = val; + break; + default: + break; + } +} + void npc_cn20k_update_action_entries_n_flags(struct rvu *rvu, struct npc_kpu_profile_adapter *pfl) { struct npc_kpu_profile_action *action; - int entries, ltype; - u8 flags, val; + int entries; for (int i = 0; i < pfl->kpus; i++) { action = pfl->kpu[i].action; entries = pfl->kpu[i].action_entries; - for (int j = 0; j < entries; j++) { - if (action[j].lid != NPC_LID_LC) - continue; - - ltype = action[j].ltype; - - if (ltype != NPC_LT_LC_IP && - ltype != NPC_LT_LC_IP6 && - ltype != NPC_LT_LC_IP_OPT && - ltype != NPC_LT_LC_IP6_EXT) - continue; - - flags = action[j].flags; - - switch (flags) { - case NPC_F_LC_U_IP_FRAG: - case NPC_F_LC_U_IP6_FRAG: - case NPC_F_LC_L_6TO4: - case NPC_F_LC_L_MPLS_IN_IP: - case NPC_F_LC_L_IP6_TUN_IP6: - case NPC_F_LC_L_IP6_MPLS_IN_IP: - val = npc_map2cn20k_flag(flags); - if (val == 0xFF) { - dev_err(rvu->dev, - "%s: Error to get flag value\n", - __func__); - return; - } - - action[j].flags = val; - break; - default: - break; - } - } + for (int j = 0; j < entries; j++) + npc_cn20k_translate_action_flags(&action[j]); } } @@ -1709,9 +1705,9 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu, for (entry = 0; entry < entries; entry++) { profile->kpu[kpu].cam[entry] = cam[entry]; profile->kpu[kpu].action[entry] = action[entry]; + npc_cn20k_translate_action_flags(&profile->kpu[kpu].action[entry]); } } - npc_cn20k_update_action_entries_n_flags(rvu, profile); return 0; } -- cgit v1.2.3