FAILED: patch "[PATCH] drm/xe: harden adjust_idledly() against divide-by-zero and overflow" failed to apply to 7.3-stable tree
Subsystems (3)
Sasha Levin
maintainerThe patch below does not apply to the 7.3-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.3.y
git checkout FETCH_HEAD
git cherry-pick -x d864065ea25e9d12897c175de9176ce46677e176
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '[email protected]' --subject-prefix 'PATCH 7.3.y' 'HEAD^..'
Thanks,
Sasha
------------------ original commit in Linus's tree ------------------
From d864065ea25e9d12897c175de9176ce46677e176 Mon Sep 17 00:00:00 2001
From: Tangudu Tilak Tirumalesh <[email protected]>
Date: Wed, 16 Sep 2026 15:35:44 +0530
Subject: [PATCH] drm/xe: harden adjust_idledly() against divide-by-zero and
overflow
adjust_idledly() has several corner-case issues flagged during review:
1. If xe_gt_clock_init() failed to recognise the crystal clock, gt->info.timestamp_base is 0, which makes idledly_units_ps also 0. The subsequent DIV_ROUND_CLOSEST(..., idledly_units_ps) is then a divide-by-zero and panics the kernel.
2. The tick-to-ns conversions are done in u32:
idledly * idledly_units_ps, (maxcnt - 1) * 1000
Both overflow u32 before DIV_ROUND_CLOSEST() sees them.
3. If IDLE_WAIT_TIME reads back as 0, maxcnt evaluates to 0 and
the maxcnt - 1 clamp wraps to 0xFFFFFFFF in u32.
4. The register only stores whole ticks, so the clamped ns value has to be converted to ticks and back. DIV_ROUND_CLOSEST() can round that conversion up past maxcnt:
maxcnt = 640 ns, one tick = 666664 ps
clamp: maxcnt - 1 = 639 ns
ns -> ticks: 639000 / 666664 = 0.958 -> rounds to 1 tick
tick -> ns: 1 * 666664 / 1000 = 667 ns
667 ns is programmed into RING_IDLEDLY, but 667 >= maxcnt (640), so xe_gt_WARN_ON() fires again on every subsequent init.
Return early if timestamp_base is 0 (the unknown-crystal path). Do the conversions in u64 via the *_ULL() helpers so they cannot wrap. Clamp with a floor (DIV_ROUND_DOWN_ULL) so the programmed delay stays strictly below maxcnt, and guard the maxcnt == 0 case with a zero delay while still writing RING_IDLEDLY so INHIBIT_SWITCH_UNTIL_PREEMPTED is cleared.
v2: Drop the redundant warn on the timestamp_base == 0 path;
xe_gt_clock_init() already warns on an unrecognised crystal clock. Keep the early return to avoid the divide-by-zero. - Vinay
v3: Field-mask the RING_IDLEDLY write with REG_FIELD_PREP(IDLE_DELAY, ...)
instead of writing the raw tick count, which could clobber INHIBIT_SWITCH_UNTIL_PREEMPTED and reserved bits. Split the inhibit-switch clear from the maxcnt clamp so a set inhibit bit no longer forces a needless delay overwrite when the delay itself is already valid. Use gt_to_xe(gt) instead of gt_to_xe(hwe->gt).
--- drivers/gpu/drm/xe/xe_hw_engine.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)
drivers/gpu/drm/xe/xe_hw_engine.c +21 -6
@@ -592,22 +592,37 @@ static void adjust_idledly(struct xe_hw_engine *hwe)u32 idledly_units_ps =8 * gt->info.timestamp_base;u32 maxcnt_units_ns =640 ;bool inhibit_switch =0 ; +bool wa_applied = false; + +if (!IS_SRIOV_VF (gt_to_xe (gt)) &&XE_GT_WA (gt,16023105232 )) { +/* xe_gt_clock_init() warns and zeroes timestamp_base on unknown crystal clock. */ +if (!idledly_units_ps) +return ; -if (!IS_SRIOV_VF (gt_to_xe (hwe->gt)) &&XE_GT_WA (gt,16023105232 )) { idledly =xe_mmio_read32 (>->mmio,RING_IDLEDLY (hwe->mmio_base)); maxcnt =xe_mmio_read32 (>->mmio,RING_PWRCTX_MAXCNT (hwe->mmio_base)); inhibit_switch = idledly &INHIBIT_SWITCH_UNTIL_PREEMPTED ; idledly =REG_FIELD_GET (IDLE_DELAY , idledly); - idledly =DIV_ROUND_CLOSEST (idledly * idledly_units_ps,1000 ); + idledly =DIV_ROUND_CLOSEST_ULL ((u64 )idledly * idledly_units_ps,1000 ); maxcnt =REG_FIELD_GET (IDLE_WAIT_TIME , maxcnt); maxcnt *= maxcnt_units_ns; -if (xe_gt_WARN_ON (gt, idledly >= maxcnt || inhibit_switch)) { - idledly =DIV_ROUND_CLOSEST (((maxcnt -1 ) *1000 ), - idledly_units_ps); -xe_mmio_write32 (>->mmio,RING_IDLEDLY (hwe->mmio_base), idledly); +/* Clear the inhibit switch without disturbing a valid delay. */ +if (inhibit_switch) + wa_applied = true; + +if (xe_gt_WARN_ON (gt, idledly >= maxcnt)) { +/* Floor below maxcnt; write 0 to still clear the inhibit bit. */ + idledly = maxcnt ? +DIV_ROUND_DOWN_ULL ((u64)(maxcnt -1 ) *1000 , + idledly_units_ps) :0 ; + wa_applied = true; } + +if (wa_applied) +xe_mmio_write32 (>->mmio,RING_IDLEDLY (hwe->mmio_base), +REG_FIELD_PREP (IDLE_DELAY , idledly)); } }
Signature · 1 line
2.53.0