On 13.08.25 13:59, Tvrtko Ursulin wrote: >> Good point. Could be that someone is using a pure kernel thread for fence >> signaling. Going to check for that instead of a worker. > > Ok, I am curious how to do it reliably. Non-null current and PF_KTHREAD and > PF_WQ_WORKER not set?
If I'm not completely mistaken (current->flags & PF_KTHREAD) should do it, but I need to double check that as well. >>> Even the fact opportunistic signalling needs to bypass the assert makes it >>> sound like there isn't anything fundamentally wrong with signalling from >>> task context. >>> >> >> Opportunistic signaling can happen from everywhere. But when an >> implementation tries to signal it from an IOCTL that is certainly invalid. >> >>> The first patch also feels a bit too much if it has no purpose apart from >>> checking the new asserts would otherwise trigger. >> >> The sw_sync code is is only used for testing and debugging. See the Kconfig >> of it: >> >> A sync object driver that uses a 32bit counter to coordinate >> synchronization. Useful when there is no hardware primitive >> backing >> the synchronization. >> >> WARNING: improper use of this can result in deadlocking kernel >> drivers from userspace. Intended for test and debug only. >> > > But it is adding kernel code for a questionable benefit. The whole sw_sync is questionable to begin with. We had that for a couple of years already until we finally realized the problem with the infinite fences. Today it should only be used for unit testing. > What about calling the non-asserting version instead of adding complexity? It > is kernel code so should be fine. That would give other implementations both the possibility and impression that this is ok. And that is something I would really like to avoid. > Because even with the worker sw_sync can still create infinite fences. Worker > just defeats the asserts so I do not see the value in complicating it. Yeah, I mean completely ripping out the sw_sync would be indeed better. But that would break existing unit tests. Regards, Christian. > > Regards, > > Tvrtko > >>> >>>> Signed-off-by: Christian König <ckoe...@able.fritz.box> >>>> --- >>>> drivers/dma-buf/dma-fence.c | 59 +++++++++++++++++++++++++++---------- >>>> include/linux/dma-fence.h | 9 ++++-- >>>> 2 files changed, 51 insertions(+), 17 deletions(-) >>>> >>>> diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c >>>> index 3f78c56b58dc..2bce620eacac 100644 >>>> --- a/drivers/dma-buf/dma-fence.c >>>> +++ b/drivers/dma-buf/dma-fence.c >>>> @@ -345,33 +345,23 @@ void __dma_fence_might_wait(void) >>>> } >>>> #endif >>>> - >>>> /** >>>> - * dma_fence_signal_timestamp_locked - signal completion of a fence >>>> + * dma_fence_signal_internal - internal signal completion of a fence >>>> * @fence: the fence to signal >>>> * @timestamp: fence signal timestamp in kernel's CLOCK_MONOTONIC time >>>> domain >>>> * >>>> - * Signal completion for software callbacks on a fence, this will unblock >>>> - * dma_fence_wait() calls and run all the callbacks added with >>>> - * dma_fence_add_callback(). Can be called multiple times, but since a >>>> fence >>>> - * can only go from the unsignaled to the signaled state and not back, it >>>> will >>>> - * only be effective the first time. Set the timestamp provided as the >>>> fence >>>> - * signal timestamp. >>>> - * >>>> - * Unlike dma_fence_signal_timestamp(), this function must be called with >>>> - * &dma_fence.lock held. >>>> + * Internal signal the dma_fence without error checking. Should *NEVER* >>>> be used >>>> + * by drivers or external code directly. >>>> * >>>> * Returns 0 on success and a negative error value when @fence has been >>>> * signalled already. >>>> */ >>>> -int dma_fence_signal_timestamp_locked(struct dma_fence *fence, >>>> - ktime_t timestamp) >>>> +int dma_fence_signal_internal(struct dma_fence *fence, ktime_t timestamp) >>>> { >>>> struct dma_fence_cb *cur, *tmp; >>>> struct list_head cb_list; >>>> lockdep_assert_held(fence->lock); >>>> - >>>> if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, >>>> &fence->flags))) >>>> return -EINVAL; >>>> @@ -390,7 +380,46 @@ int dma_fence_signal_timestamp_locked(struct >>>> dma_fence *fence, >>>> return 0; >>>> } >>>> -EXPORT_SYMBOL(dma_fence_signal_timestamp_locked); >>>> +EXPORT_SYMBOL(dma_fence_signal_internal); >>>> + >>>> +/** >>>> + * dma_fence_signal_timestamp_locked - signal completion of a fence >>>> + * @fence: the fence to signal >>>> + * @timestamp: fence signal timestamp in kernel's CLOCK_MONOTONIC time >>>> domain >>>> + * >>>> + * Signal completion for software callbacks on a fence, this will unblock >>>> + * dma_fence_wait() calls and run all the callbacks added with >>>> + * dma_fence_add_callback(). Can be called multiple times, but since a >>>> fence >>>> + * can only go from the unsignaled to the signaled state and not back, it >>>> will >>>> + * only be effective the first time. Set the timestamp provided as the >>>> fence >>>> + * signal timestamp. >>>> + * >>>> + * Unlike dma_fence_signal_timestamp(), this function must be called with >>>> + * &dma_fence.lock held. >>>> + * >>>> + * Returns 0 on success and a negative error value when @fence has been >>>> + * signalled already. >>>> + */ >>>> +int dma_fence_signal_timestamp_locked(struct dma_fence *fence, >>>> + ktime_t timestamp) >>>> +{ >>>> + /* >>>> + * We have the re-occurring problem that people try to invent a >>>> + * DMA-fences implementation which signals fences based on an >>>> userspace >>>> + * IOCTL. >>>> + * >>>> + * This is well known as source of hard to track down crashes and is >>>> + * documented to be an invalid approach. The problem is that it seems >>>> + * to work during initial testing and only long term tests points out >>>> + * why this can never work correctly. >>>> + * >>>> + * So give at least a warning when people try to signal a fence from >>>> + * task context and not from interrupts or a work item. This check is >>>> + * certainly not perfect but better than nothing. >>>> + */ >>>> + WARN_ON_ONCE(!in_interrupt() && !current_work()); >>>> + return dma_fence_signal_internal(fence, timestamp); >>>> +} >>>> /** >>>> * dma_fence_signal_timestamp - signal completion of a fence >>>> diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h >>>> index 64639e104110..8dbcd66989b8 100644 >>>> --- a/include/linux/dma-fence.h >>>> +++ b/include/linux/dma-fence.h >>>> @@ -369,6 +369,7 @@ int dma_fence_signal_locked(struct dma_fence *fence); >>>> int dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t >>>> timestamp); >>>> int dma_fence_signal_timestamp_locked(struct dma_fence *fence, >>>> ktime_t timestamp); >>>> +int dma_fence_signal_internal(struct dma_fence *fence, ktime_t timestamp); >>>> signed long dma_fence_default_wait(struct dma_fence *fence, >>>> bool intr, signed long timeout); >>>> int dma_fence_add_callback(struct dma_fence *fence, >>>> @@ -422,7 +423,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence) >>>> return true; >>>> if (fence->ops->signaled && fence->ops->signaled(fence)) { >>>> - dma_fence_signal_locked(fence); >>>> + dma_fence_signal_internal(fence, ktime_get()); >>>> return true; >>>> } >>>> @@ -448,11 +449,15 @@ dma_fence_is_signaled_locked(struct dma_fence >>>> *fence) >>>> static inline bool >>>> dma_fence_is_signaled(struct dma_fence *fence) >>>> { >>>> + unsigned long flags; >>>> + >>>> if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) >>>> return true; >>>> if (fence->ops->signaled && fence->ops->signaled(fence)) { >>>> - dma_fence_signal(fence); >>>> + spin_lock_irqsave(fence->lock, flags); >>>> + dma_fence_signal_internal(fence, ktime_get()); >>>> + spin_unlock_irqrestore(fence->lock, flags); >>>> return true; >>>> } >>>> >>> >> >