[PATCH net 2/2] dpll: filter pin->dpll_refs by cookie in dpll_pin_on_pin_unregister
From: Petr Oros
Date: Sat May 16 2026 - 15:13:59 EST
dpll_pin_on_pin_register() iterates parent->dpll_refs and adds the
child pin to each dpll the parent contributes to, tagging the
registration with cookie = parent. The unregister side asymmetrically
iterates pin->dpll_refs (a union across all of pin's parents) and
calls __dpll_pin_unregister() on every entry without checking that
the entry carries a (ops, priv, parent) registration.
When the same pin is reachable through multiple parents whose
supported-dpll sets differ, unregistering one parent walks entries
owned by other parents and trips WARN_ON(!reg) in
dpll_xa_ref_pin_del():
parent_A in {dpll_X}, parent_B in {dpll_X, dpll_Y}
pin reachable through both
pin->dpll_refs = {dpll_X, dpll_Y}
dpll_X has cookies {parent_A, parent_B}
dpll_Y has cookie {parent_B}
unregister(parent_A) iterates {dpll_X, dpll_Y}; the dpll_Y step
looks up cookie = parent_A which was never there -> WARN.
WARNING: CPU: 137 PID: 4498 at drivers/dpll/dpll_core.c:252 dpll_xa_ref_pin_del.isra.0+0x1b0/0x1c0
Call Trace:
<TASK>
__dpll_pin_unregister+0xed/0x2e0
dpll_pin_on_pin_unregister+0x91/0xe0
ice_dpll_deinit_pins+0xb2/0x400 [ice]
ice_dpll_deinit+0x32/0x130 [ice]
ice_deinit_features.part.0+0xb6/0x100 [ice]
ice_unload+0xdf/0x120 [ice]
ice_remove+0x144/0x2f0 [ice]
ice_shutdown+0x16/0x50 [ice]
pci_device_shutdown+0x34/0x60
device_shutdown+0x158/0x200
kernel_kexec+0x48/0x160
__do_sys_reboot+0xda/0x230
do_syscall_64+0xec/0x680
entry_SYSCALL_64_after_hwframe+0x76/0x7e
</TASK>
Filter the iteration by the (ops, priv, parent) cookie so only the
entries this parent actually contributed to get touched. Keep
pin->dpll_refs as the iteration source (rather than parent->dpll_refs
as in register) so cleanup is also correct when the parent has been
independently unregistered from some of its dpll devices in the
meantime; switching sides would silently leak those registrations.
The fix is placed at the call site rather than relaxing WARN_ON(!reg)
in dpll_xa_ref_pin_del(): other callers (dpll_pin_unregister() with
cookie = NULL, the dpll_pin_on_pin_register() error rollback with
cookie = parent) pass cookies that must be present, and the WARN
remains useful there as a double-unregister catch.
Reproduced on E825-C + zl3073x via kexec stress: WARN appears within
1-4 reboots without the fix, none across several hundred reboots
with it.
Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions")
Signed-off-by: Petr Oros <poros@xxxxxxxxxx>
---
drivers/dpll/dpll_core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 4a058b46c69d4f..4779976682fdb9 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -1024,8 +1024,14 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
mutex_lock(&dpll_lock);
dpll_pin_delete_ntf(pin);
dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin);
- xa_for_each(&pin->dpll_refs, i, ref)
+ /* pin->dpll_refs is the union over all of pin's parents; only
+ * touch entries actually registered via @parent.
+ */
+ xa_for_each(&pin->dpll_refs, i, ref) {
+ if (!dpll_pin_registration_find(ref, ops, priv, parent))
+ continue;
__dpll_pin_unregister(ref->dpll, pin, ops, priv, parent);
+ }
mutex_unlock(&dpll_lock);
}
EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
--
2.53.0