* Re: [PATCH v18 20/42] dept: apply timeout consideration to dma fence wait
2026-03-11 16:38 ` [PATCH v18 20/42] dept: apply timeout consideration to dma fence wait Yunseong Kim
@ 2026-03-12 1:44 ` Byungchul Park
0 siblings, 0 replies; 2+ messages in thread
From: Byungchul Park @ 2026-03-12 1:44 UTC (permalink / raw)
To: Yunseong Kim
Cc: Nathan Chancellor, kernel_team, torvalds, gregkh, kernel-team,
linux-mm, minchan, harry.yoo, gwan-gyeong.mun, max.byungchul.park,
Peter Zijlstra, Ingo Molnar, Will Deacon, boqun.feng, Waiman Long,
yunseong.kim, yeoreum.yun, Maarten Lankhorst, Maxime Ripard,
Matthew Brost, Thomas Zimmermann, David Airlie, Simona Vetter,
Thomas Hellström, Rodrigo Vivi, Nick Desaulniers,
Bill Wendling, Justin Stitt, Stuart Summers, Nirmoy Das, intel-xe,
dri-devel, llvm, patches, linux-kernel
On Thu, Mar 12, 2026 at 01:38:19AM +0900, Yunseong Kim wrote:
> Hi all,
>
> I have been investigating repeated Clang build failures of the form:
>
> "cannot jump from this indirect goto statement to one of its possible targets"
> "note: jump enters a statement expression"
>
> triggered by the interaction between drm_exec's indirect goto
> (goto *__drm_exec_retry_ptr) and diagnostic macros such as _THIS_IP_,
> LOCKDEP, and DEPT.
>
> Link: https://lore.kernel.org/all/?q=b%3A%22error%3A+cannot+jump+from+this+indirect+goto+statement+to+one+of+its+possible+targets%22
>
> Notably, failures on DEPT are currently observed on arm64 builds.
> On arm64, _THIS_IP_ uses the common implementation based on a GNU C
> statement expression with an address-taken label, relying on compiler
> optimization rather than an architecture-specific definition. This makes
> arm64 particularly sensitive to Clang's treatment of address-taken labels
> and indirect goto targets.
>
> This is also not limited to a single driver. I have observed the same
> failure pattern in multiple DRM drivers when DEPT instrumentation is in
> the call path (e.g. dma_fence_wait() -> sdt_might_sleep_start_timeout()
> -> _THIS_IP_), including msm and others.
>
> $ clang --version
> clang version 23.0.0git (https://github.com/llvm/llvm-project.git
> 1ff1e5f10a5c765b4cf1344c4964604dcd09fef3)
>
> drivers/gpu/drm/msm/msm_gem_vma.c:919:3: error: cannot jump from this
> indirect goto statement to one of its possible targets
> 919 | drm_exec_retry_on_contention(&exec);
> | ^
> ./include/drm/drm_exec.h:123:4: note: expanded from macro
> 'drm_exec_retry_on_contention'
> 123 | goto *__drm_exec_retry_ptr; \
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: possible target of
> indirect goto statement
> 909 | dma_fence_wait(vm->last_fence, false);
> | ^
> ./include/linux/dma-fence.h:677:2: note: expanded from macro 'dma_fence_wait'
> 677 | sdt_might_sleep_start_timeout(NULL,
> MAX_SCHEDULE_TIMEOUT); \
> | ^
> ./include/linux/dept_sdt.h:46:31: note: expanded from macro
> 'sdt_might_sleep_start_timeout'
> 46 | unsigned long __this_ip__ = _THIS_IP_;
> \
> | ^
> ./include/linux/instruction_pointer.h:10:41: note: expanded from macro
> '_THIS_IP_'
> 10 | #define _THIS_IP_ ({ __label__ __here; __here: (unsigned
> long)&&__here; })
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: jump enters a statement
> expression
> ./include/linux/dma-fence.h:677:2: note: expanded from macro 'dma_fence_wait'
> 677 | sdt_might_sleep_start_timeout(NULL,
> MAX_SCHEDULE_TIMEOUT); \
> | ^
> ./include/linux/dept_sdt.h:46:31: note: expanded from macro
> 'sdt_might_sleep_start_timeout'
> 46 | unsigned long __this_ip__ = _THIS_IP_;
> \
> | ^
> ./include/linux/instruction_pointer.h:10:20: note: expanded from macro
> '_THIS_IP_'
> 10 | #define _THIS_IP_ ({ __label__ __here; __here: (unsigned
> long)&&__here; })
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: jump enters a statement
> expression
> ./include/linux/dma-fence.h:673:38: note: expanded from macro 'dma_fence_wait'
> 673 | #define dma_fence_wait(f, intr)
> \
> |
> ^
> drivers/gpu/drm/msm/msm_gem_vma.c:933:5: error: cannot jump from this
> indirect goto statement to one of its possible targets
> 933 | drm_exec_retry_on_contention(&exec);
> | ^
> ./include/drm/drm_exec.h:123:4: note: expanded from macro
> 'drm_exec_retry_on_contention'
> 123 | goto *__drm_exec_retry_ptr; \
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: possible target of
> indirect goto statement
> 909 | dma_fence_wait(vm->last_fence, false);
> | ^
> ./include/linux/dma-fence.h:677:2: note: expanded from macro 'dma_fence_wait'
> 677 | sdt_might_sleep_start_timeout(NULL,
> MAX_SCHEDULE_TIMEOUT); \
> | ^
> ./include/linux/dept_sdt.h:46:31: note: expanded from macro
> 'sdt_might_sleep_start_timeout'
> 46 | unsigned long __this_ip__ = _THIS_IP_;
> \
> | ^
> ./include/linux/instruction_pointer.h:10:41: note: expanded from macro
> '_THIS_IP_'
> 10 | #define _THIS_IP_ ({ __label__ __here; __here: (unsigned
> long)&&__here; })
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: jump enters a statement
> expression
> ./include/linux/dma-fence.h:677:2: note: expanded from macro 'dma_fence_wait'
> 677 | sdt_might_sleep_start_timeout(NULL,
> MAX_SCHEDULE_TIMEOUT); \
> | ^
> ./include/linux/dept_sdt.h:46:31: note: expanded from macro
> 'sdt_might_sleep_start_timeout'
> 46 | unsigned long __this_ip__ = _THIS_IP_;
> \
> | ^
> ./include/linux/instruction_pointer.h:10:20: note: expanded from macro
> '_THIS_IP_'
> 10 | #define _THIS_IP_ ({ __label__ __here; __here: (unsigned
> long)&&__here; })
> | ^
> drivers/gpu/drm/msm/msm_gem_vma.c:909:3: note: jump enters a statement
> expression
> ./include/linux/dma-fence.h:673:38: note: expanded from macro 'dma_fence_wait'
> 673 | #define dma_fence_wait(f, intr)
> \
> |
> ^
>
> I was able to find the pattern of the kernel source code that uses this pattern:
>
> $ git grep -n 'goto \*' | grep ';' | grep -v "\*/" | \
> grep -Ev "(\*[[:space:]]+goto|_goto.*\*\);)"
> drivers/gpu/drm/xe/xe_validation.h:149: goto *__drm_exec_retry_ptr; \
> drivers/misc/lkdtm/cfi.c:129: goto *labels[1];
> drivers/misc/lkdtm/cfi.c:131: goto *labels[2];
> drivers/misc/lkdtm/cfi.c:133: goto *labels[3];
> drivers/misc/lkdtm/cfi.c:135: goto *labels[4];
> include/drm/drm_exec.h:123: goto *__drm_exec_retry_ptr; \
> kernel/bpf/core.c:1776: goto *jumptable[insn->code];
> scripts/gcc-plugins/gcc-common.h:368: return as_a<ggoto *>(stmt);
> scripts/gcc-plugins/gcc-common.h:373: return as_a<const ggoto *>(stmt);
> tools/testing/selftests/bpf/progs/bpf_gotox.c:219: goto *jt[ctx->x & 0xff];
> tools/testing/selftests/bpf/progs/bpf_gotox.c:261: goto *jt1[a];
> tools/testing/selftests/bpf/progs/bpf_gotox.c:263: goto *jt2[b];
> tools/testing/selftests/bpf/progs/bpf_gotox.c:284: goto *jt[a];
> tools/testing/selftests/bpf/progs/bpf_gotox.c:287: goto *jt[a + b];
>
> I understand that Clang's behavior here is intentional and conservative:
> any address-taken label in the same function is treated as a potential
> indirect goto target, and jumping into a statement expression is diagnosed
> to avoid semantic issues. This aligns with:
>
> - [Clang] Diagnose jumps into statement expressions (D154696)
> https://reviews.llvm.org/D154696
>
> At the same time, the kernel is not using _THIS_IP_ for control flow.
> The label address is used purely for diagnostics (lockdep/DEPT attribution).
> However, as discussed in:
>
> - LLVM Issue #138272: Add builtin/intrinsic to get current instruction pointer
> https://github.com/llvm/llvm-project/issues/138272
>
> using blockaddress (&&label) purely to obtain an instruction pointer is
> problematic in LLVM's model, since blockaddress has defined behavior only
> when used with indirectbr or for null comparisons.
>
> Similar issues have also been observed in other contexts where indirect
> goto interacts with address-taken labels, e.g.:
>
> - LLVM Issue #28019: Wrong 'cannot jump from this indirect goto statement'
> https://github.com/llvm/llvm-project/issues/28019
>
> Given this, I think there is three possible directions:
Hi,
Thanks for the clarification of the issue.
Indeed, the issue is inevitable as long as 'THIS_IP' macro is used along
with 'indirect goto' statement nearby.
> 1) Continue per-callsite structural workarounds
> Keep separating indirect goto usage from any code that expands _THIS_IP_
> (e.g. moving PROVE_LOCKING / DEPT paths into helper functions). This avoids
> the diagnostic but requires ongoing refactoring and does not scale well as
> similar patterns reappear across drivers.
Yes. This workaround would work anyway. However, hopefully, we can
resolve it in a better way if any.
> 2) Introduce a compiler-supported alternative to _THIS_IP_
> As discussed in LLVM Issue #138272, a dedicated builtin or intrinsic to
> obtain a best-effort instruction pointer for diagnostics would allow the
> kernel to avoid address-taken labels entirely and eliminate this class of
> conflicts.
Or introduce a new attribute to indicate that 'the symbol(label) is not
used by indirect goto from out of the scope' or something?
> 3) DRM/drm_exec-scoped refactoring as a pragmatic middle ground
> Since drm_exec is the common convergence point where indirect goto and
> DEPT/LOCKDEP instrumentation can meet in the same function across multiple
> DRM drivers, we could refactor the drm_exec usage patterns (or provide a
> recommended wrapper pattern) to structurally separate the retry/indirect-goto
> region from instrumentation that expands _THIS_IP_. This would avoid
> kernel-wide churn while addressing the recurring failures in DRM drivers.
>
> I am not proposing to weaken Clang's diagnostics. Rather, I would like
> feedback on whether option (3) is considered an acceptable short- to
> medium-term approach, and whether option (2) is the preferred long-term
> direction from the compiler side.
I'd also like to hear feedbacks for what you mentioned.
Thanks!
Byungchul
> Any guidance from the LOCKDEP and kernel LLVM maintainers on the preferred
> path forward would be greatly appreciated.
>
> On 12/5/25 4:18 PM, Byungchul Park wrote:
> > Now that CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT was introduced, apply the
> > consideration to dma fence wait.
> >
> > Signed-off-by: Byungchul Park <byungchul@sk.com>
> > ---
> > drivers/dma-buf/dma-fence.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
> > index b313bb59dc9c..f2cc7068af65 100644
> > --- a/drivers/dma-buf/dma-fence.c
> > +++ b/drivers/dma-buf/dma-fence.c
> > @@ -799,7 +799,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
> > cb.task = current;
> > list_add(&cb.base.node, &fence->cb_list);
> >
> > - sdt_might_sleep_start(NULL);
> > + sdt_might_sleep_start_timeout(NULL, timeout);
> > while (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) {
> > if (intr)
> > __set_current_state(TASK_INTERRUPTIBLE);
> > @@ -903,7 +903,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
> > }
> > }
> >
> > - sdt_might_sleep_start(NULL);
> > + sdt_might_sleep_start_timeout(NULL, timeout);
> > while (ret > 0) {
> > if (intr)
> > set_current_state(TASK_INTERRUPTIBLE);
>
>
> Best regards,
> Yuseong
^ permalink raw reply [flat|nested] 2+ messages in thread