[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250814094824.217142-10-janusz.krzysztofik@linux.intel.com>
Date: Thu, 14 Aug 2025 10:16:15 +0200
From: Janusz Krzysztofik <janusz.krzysztofik@...ux.intel.com>
To: Christian König <christian.koenig@....com>
Cc: Sumit Semwal <sumit.semwal@...aro.org>,
Gustavo Padovan <gustavo@...ovan.org>,
Chris Wilson <chris.p.wilson@...ux.intel.com>,
linux-media@...r.kernel.org,
dri-devel@...ts.freedesktop.org,
linaro-mm-sig@...ts.linaro.org,
linux-kernel@...r.kernel.org,
intel-gfx@...ts.freedesktop.org,
intel-xe@...ts.freedesktop.org,
Janusz Krzysztofik <janusz.krzysztofik@...ux.intel.com>
Subject: [PATCH 4/4] dma-buf/fence-chain: Speed up processing of rearmed callbacks
When first user starts waiting on a not yet signaled fence of a chain
link, a dma_fence_chain callback is added to a user fence of that link.
When the user fence of that chain link is then signaled, the chain is
traversed in search for a first not signaled link and the callback is
rearmed on a user fence of that link.
Since chain fences may be exposed to user space, e.g. over drm_syncobj
IOCTLs, users may start waiting on any link of the chain, then many links
of a chain may have signaling enabled and their callbacks added to their
user fences. Once an arbitrary user fence is signaled, all
dma_fence_chain callbacks added to it so far must be rearmed to another
user fence of the chain. In extreme scenarios, when all N links of a
chain are awaited and then signaled in reverse order, the dma_fence_chain
callback may be called up to N * (N + 1) / 2 times (an arithmetic series).
To avoid that potential excessive accumulation of dma_fence_chain
callbacks, rearm a trimmed-down, signal only callback version to the base
fence of a previous link, if not yet signaled, otherwise just signal the
base fence of the current link instead of traversing the chain in search
for a first not signaled link and moving all callbacks collected so far to
a user fence of that link.
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12904
Suggested-by: Chris Wilson <chris.p.wilson@...ux.intel.com>
Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@...ux.intel.com>
---
drivers/dma-buf/dma-fence-chain.c | 101 +++++++++++++++++++++++++-----
1 file changed, 84 insertions(+), 17 deletions(-)
diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c
index a8a90acf4f34d..90eff264ee05c 100644
--- a/drivers/dma-buf/dma-fence-chain.c
+++ b/drivers/dma-buf/dma-fence-chain.c
@@ -119,46 +119,113 @@ static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
return "unbound";
}
-static void dma_fence_chain_irq_work(struct irq_work *work)
+static void signal_irq_work(struct irq_work *work)
{
struct dma_fence_chain *chain;
chain = container_of(work, typeof(*chain), work);
- /* Try to rearm the callback */
- if (!dma_fence_chain_enable_signaling(&chain->base))
- /* Ok, we are done. No more unsignaled fences left */
- dma_fence_signal(&chain->base);
+ dma_fence_signal(&chain->base);
dma_fence_put(&chain->base);
}
-static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
+static void signal_cb(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+ struct dma_fence_chain *chain;
+
+ chain = container_of(cb, typeof(*chain), cb);
+ init_irq_work(&chain->work, signal_irq_work);
+ irq_work_queue(&chain->work);
+}
+
+static void rearm_irq_work(struct irq_work *work)
+{
+ struct dma_fence_chain *chain;
+ struct dma_fence *prev;
+
+ chain = container_of(work, typeof(*chain), work);
+
+ rcu_read_lock();
+ prev = rcu_dereference(chain->prev);
+ if (prev && dma_fence_add_callback(prev, &chain->cb, signal_cb))
+ prev = NULL;
+ rcu_read_unlock();
+ if (prev)
+ return;
+
+ /* Ok, we are done. No more unsignaled fences left */
+ signal_irq_work(work);
+}
+
+static inline bool fence_is_signaled__nested(struct dma_fence *fence)
+{
+ if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
+ return true;
+
+ if (fence->ops->signaled && fence->ops->signaled(fence)) {
+ unsigned long flags;
+
+ spin_lock_irqsave_nested(fence->lock, flags, SINGLE_DEPTH_NESTING);
+ dma_fence_signal_locked(fence);
+ spin_unlock_irqrestore(fence->lock, flags);
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool prev_is_signaled(struct dma_fence_chain *chain)
+{
+ struct dma_fence *prev;
+ bool result;
+
+ rcu_read_lock();
+ prev = rcu_dereference(chain->prev);
+ result = !prev || fence_is_signaled__nested(prev);
+ rcu_read_unlock();
+
+ return result;
+}
+
+static void rearm_or_signal_cb(struct dma_fence *f, struct dma_fence_cb *cb)
{
struct dma_fence_chain *chain;
chain = container_of(cb, typeof(*chain), cb);
- init_irq_work(&chain->work, dma_fence_chain_irq_work);
+ if (prev_is_signaled(chain)) {
+ /* Ok, we are done. No more unsignaled fences left */
+ init_irq_work(&chain->work, signal_irq_work);
+ } else {
+ /* Try to rearm the callback */
+ init_irq_work(&chain->work, rearm_irq_work);
+ }
+
irq_work_queue(&chain->work);
- dma_fence_put(f);
}
static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
{
struct dma_fence_chain *head = to_dma_fence_chain(fence);
+ int err = -ENOENT;
- dma_fence_get(&head->base);
- dma_fence_chain_for_each(fence, &head->base) {
- struct dma_fence *f = dma_fence_chain_contained(fence);
+ if (WARN_ON(!head))
+ return false;
- dma_fence_get(f);
- if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
+ dma_fence_get(fence);
+ if (head->fence)
+ err = dma_fence_add_callback(head->fence, &head->cb, rearm_or_signal_cb);
+ if (err) {
+ if (prev_is_signaled(head)) {
dma_fence_put(fence);
- return true;
+ } else {
+ init_irq_work(&head->work, rearm_irq_work);
+ irq_work_queue(&head->work);
+ err = 0;
}
- dma_fence_put(f);
}
- dma_fence_put(&head->base);
- return false;
+
+ return !err;
}
static bool dma_fence_chain_signaled(struct dma_fence *fence)
--
2.50.1
Powered by blists - more mailing lists