* [PATCH v2 1/3] drm/vc4: Drop BO usecnt before releasing the old FB
2026-11-14 19:16 [PATCH v2 0/3] drm/vc4: Fix async page flip completion Maíra Canal
@ 2026-11-14 19:16 ` Maíra Canal
2026-11-14 19:16 ` [PATCH v2 2/3] drm/vc4: Complete async page flips from an ordered workqueue Maíra Canal
2026-11-14 19:16 ` [PATCH v2 3/3] drm/vc4: Look up the async page flip fence before committing the flip Maíra Canal
2 siblings, 0 replies; 5+ messages in thread
From: Maíra Canal @ 2026-11-14 19:16 UTC (permalink / raw)
To: Iago Toral, Melissa Wen, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel-dev, dri-devel, stable, Maíra Canal
The async page flip completion drops the old FB reference before
decrementing its BO usecnt. If that was the last reference, the BO is
already freed or recycled into the BO cache when vc4_bo_dec_usecnt()
runs, leading to a use-after-free or a refcount underflow. For example,
while running glmark2-es2-drm in the Raspberry Pi 3, the following
warning appears:
[ 459.078834] ------------[ cut here ]------------
[ 459.078855] refcount_t: underflow; use-after-free.
[ 459.078952] WARNING: lib/refcount.c:96 at refcount_dec_not_one+0xa0/0xa8, CPU#0: swapper/0/0
<snip>
[ 459.079316] Call trace:
[ 459.079323] refcount_dec_not_one+0xa0/0xa8 (P)
[ 459.079335] vc4_bo_dec_usecnt+0x30/0x118 [vc4]
[ 459.079371] vc4_async_page_flip_complete_with_cleanup+0xa0/0xc4 [vc4]
[ 459.079395] dma_fence_signal_timestamp_locked+0xb0/0x12c
[ 459.079408] dma_fence_signal+0x3c/0x6c
[ 459.079416] vc4_irq_render_job_done+0x24/0xbc [vc4]
[ 459.079440] vc4_irq+0x120/0x138 [vc4]
This could happen when userspace removes the old FB and closes its GEM
handle while the flip is still pending.
Decrement the BO usecnt before releasing the old FB reference. Do this
by unifying the two fence callbacks and only decrementing the BO usecnt
in VC4_GEN_4.
Cc: stable@vger.kernel.org
Fixes: 4d12c36fb73b ("drm/vc4: crtc: Move the BO handling out of common page-flip callback")
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 62 +++++++++++++++---------------------------
1 file changed, 22 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index f42cd5353230..c558b527c7e6 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -895,6 +895,7 @@ vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
{
struct drm_crtc *crtc = flip_state->crtc;
struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_plane *plane = crtc->primary;
vc4_plane_async_set_fb(plane, flip_state->fb);
@@ -909,39 +910,27 @@ vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
drm_crtc_vblank_put(crtc);
drm_framebuffer_put(flip_state->fb);
- if (flip_state->old_fb)
- drm_framebuffer_put(flip_state->old_fb);
-
- kfree(flip_state);
-}
-
-static void vc4_async_page_flip_complete_with_cleanup(struct dma_fence *fence,
- struct dma_fence_cb *cb)
-{
- struct vc4_async_flip_state *flip_state =
- container_of(cb, struct vc4_async_flip_state, cb);
- struct vc4_bo *bo = NULL;
-
if (flip_state->old_fb) {
- struct drm_gem_dma_object *dma_bo =
- drm_fb_dma_get_gem_obj(flip_state->old_fb, 0);
- bo = to_vc4_bo(&dma_bo->base);
+ /*
+ * Decrement the BO usecnt in order to keep the inc/dec
+ * calls balanced when the planes are updated through
+ * the async update path.
+ *
+ * FIXME: we should move to generic async-page-flip when
+ * it's available, so that we can get rid of this
+ * hand-made cleanup_fb() logic.
+ */
+ if (vc4->gen == VC4_GEN_4) {
+ struct drm_gem_dma_object *dma_bo =
+ drm_fb_dma_get_gem_obj(flip_state->old_fb, 0);
+
+ vc4_bo_dec_usecnt(to_vc4_bo(&dma_bo->base));
+ }
+
+ drm_framebuffer_put(flip_state->old_fb);
}
- vc4_async_page_flip_complete(flip_state);
- dma_fence_put(fence);
-
- /*
- * Decrement the BO usecnt in order to keep the inc/dec
- * calls balanced when the planes are updated through
- * the async update path.
- *
- * FIXME: we should move to generic async-page-flip when
- * it's available, so that we can get rid of this
- * hand-made cleanup_fb() logic.
- */
- if (bo)
- vc4_bo_dec_usecnt(bo);
+ kfree(flip_state);
}
static void vc4_async_page_flip_fence_complete(struct dma_fence *fence,
@@ -959,30 +948,23 @@ static int vc4_async_set_fence_cb(struct drm_device *dev,
{
struct drm_framebuffer *fb = flip_state->fb;
struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0);
- dma_fence_func_t async_page_flip_complete_function;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
struct dma_fence *fence;
int ret;
- if (vc4->gen == VC4_GEN_4)
- async_page_flip_complete_function = vc4_async_page_flip_complete_with_cleanup;
- else
- async_page_flip_complete_function = vc4_async_page_flip_fence_complete;
-
ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ, &fence);
if (ret)
return ret;
/* If there's no fence, complete the page flip immediately */
if (!fence) {
- async_page_flip_complete_function(fence, &flip_state->cb);
+ vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
return 0;
}
/* If the fence has already been completed, complete the page flip */
if (dma_fence_add_callback(fence, &flip_state->cb,
- async_page_flip_complete_function))
- async_page_flip_complete_function(fence, &flip_state->cb);
+ vc4_async_page_flip_fence_complete))
+ vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
return 0;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 2/3] drm/vc4: Complete async page flips from an ordered workqueue
2026-11-14 19:16 [PATCH v2 0/3] drm/vc4: Fix async page flip completion Maíra Canal
2026-11-14 19:16 ` [PATCH v2 1/3] drm/vc4: Drop BO usecnt before releasing the old FB Maíra Canal
@ 2026-11-14 19:16 ` Maíra Canal
2026-11-14 19:40 ` sashiko-bot
2026-11-14 19:16 ` [PATCH v2 3/3] drm/vc4: Look up the async page flip fence before committing the flip Maíra Canal
2 siblings, 1 reply; 5+ messages in thread
From: Maíra Canal @ 2026-11-14 19:16 UTC (permalink / raw)
To: Iago Toral, Melissa Wen, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel-dev, dri-devel, stable, Maíra Canal
Async page flips complete from a dma_fence callback, which runs in the
fence signaling context: the VC4 render job IRQ handler on RPi 0-3 since
commit e8941ac97f28 ("drm/vc4: Remove BOs seqnos"), and the V3D IRQ
handler on RPi 4-5 since commit d19e00ee06a9 ("drm/vc4: crtc: Fix out of
order frames during asynchronous page flips").
Completing the flip may sleep: on RPi 0-3, vc4_bo_dec_usecnt() takes
bo->madv_lock, and on all platforms, dropping the last framebuffer
reference takes fb_lock and can free the BO. When running the kernel
with CONFIG_DEBUG_ATOMIC_SLEEP=y on RPi 3, this triggers the following
warning:
[ 2511.636319] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:623
[ 2511.636667] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/0
[ 2511.636681] preempt_count: 1000002, expected: 0
[ 2511.636689] RCU nest depth: 0, expected: 0
[ 2511.636698] locks held by swapper/0/0: 1, on CPU#0:
[ 2511.636711] #0: ffff000037183198 (&fence->inline_lock){-...}-{3:3}, at: dma_fence_signal+0x88/0xec
<snip>
[ 2511.636892] Hardware name: Raspberry Pi 3 Model B Plus Rev 1.3 (DT)
[ 2511.636899] Call trace:
<snip>
[ 2511.636991] mutex_lock_nested+0x24/0x30
[ 2511.637002] vc4_bo_dec_usecnt+0x40/0x124 [vc4]
[ 2511.637070] vc4_async_page_flip_complete_with_cleanup+0xa0/0xc4 [vc4]
[ 2511.637102] dma_fence_signal_timestamp_locked+0x118/0x290
[ 2511.637116] dma_fence_signal+0x9c/0xec
[ 2511.637126] vc4_irq_render_job_done+0x24/0x114 [vc4]
[ 2511.637158] vc4_irq+0x16c/0x274 [vc4]
Make the fence callback only queue a work item on a per-CRTC ordered
workqueue and complete the flip from it. Flips with no fence or an already
signaled one still complete inline, but only after flushing that workqueue,
so they can't complete before earlier flips on the same CRTC whose fence
already signaled.
Cc: stable@vger.kernel.org
Fixes: d19e00ee06a9 ("drm/vc4: crtc: Fix out of order frames during asynchronous page flips")
Fixes: e8941ac97f28 ("drm/vc4: Remove BOs seqnos")
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 51 ++++++++++++++++++++++++++----------------
drivers/gpu/drm/vc4/vc4_drv.h | 6 +++++
2 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index c558b527c7e6..177ee27ab6d1 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -884,15 +884,18 @@ struct vc4_async_flip_state {
struct drm_framebuffer *fb;
struct drm_framebuffer *old_fb;
struct drm_pending_vblank_event *event;
+ struct dma_fence *fence;
struct dma_fence_cb cb;
+ struct work_struct work;
};
-/* Called when the V3D execution for the BO being flipped to is done, so that
- * we can actually update the plane's address to point to it.
+/* Executed when the V3D execution for the BO being flipped to is done, so
+ * that we can actually update the plane's address to point to it.
*/
-static void
-vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
+static void vc4_async_page_flip_work(struct work_struct *work)
{
+ struct vc4_async_flip_state *flip_state =
+ container_of(work, struct vc4_async_flip_state, work);
struct drm_crtc *crtc = flip_state->crtc;
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
@@ -930,17 +933,19 @@ vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
drm_framebuffer_put(flip_state->old_fb);
}
+ dma_fence_put(flip_state->fence);
kfree(flip_state);
}
-static void vc4_async_page_flip_fence_complete(struct dma_fence *fence,
- struct dma_fence_cb *cb)
+static void vc4_async_page_flip_fence_cb(struct dma_fence *fence,
+ struct dma_fence_cb *cb)
{
struct vc4_async_flip_state *flip_state =
container_of(cb, struct vc4_async_flip_state, cb);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(flip_state->crtc);
- vc4_async_page_flip_complete(flip_state);
- dma_fence_put(fence);
+ /* Completing the flip may sleep, so defer it to a worker. */
+ queue_work(vc4_crtc->async_flip_wq, &flip_state->work);
}
static int vc4_async_set_fence_cb(struct drm_device *dev,
@@ -948,23 +953,26 @@ static int vc4_async_set_fence_cb(struct drm_device *dev,
{
struct drm_framebuffer *fb = flip_state->fb;
struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0);
- struct dma_fence *fence;
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(flip_state->crtc);
int ret;
- ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ, &fence);
+ INIT_WORK(&flip_state->work, vc4_async_page_flip_work);
+
+ ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ,
+ &flip_state->fence);
if (ret)
return ret;
- /* If there's no fence, complete the page flip immediately */
- if (!fence) {
- vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
+ if (flip_state->fence &&
+ !dma_fence_add_callback(flip_state->fence, &flip_state->cb,
+ vc4_async_page_flip_fence_cb))
return 0;
- }
- /* If the fence has already been completed, complete the page flip */
- if (dma_fence_add_callback(fence, &flip_state->cb,
- vc4_async_page_flip_fence_complete))
- vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
+ /* No fence or the fence has already signaled. Complete the flip
+ * immediately, after earlier queued flips.
+ */
+ flush_workqueue(vc4_crtc->async_flip_wq);
+ vc4_async_page_flip_work(&flip_state->work);
return 0;
}
@@ -990,7 +998,7 @@ vc4_async_page_flip_common(struct drm_crtc *crtc,
/* Save the current FB before it's replaced by the new one in
* drm_atomic_set_fb_for_plane(). We'll need the old FB in
- * vc4_async_page_flip_complete() to decrement the BO usecnt and keep
+ * vc4_async_page_flip_work() to decrement the BO usecnt and keep
* it consistent.
* FIXME: we should move to generic async-page-flip when it's
* available, so that we can get rid of this hand-made cleanup_fb()
@@ -1395,6 +1403,11 @@ int __vc4_crtc_init(struct drm_device *drm,
drm_crtc_helper_add(crtc, crtc_helper_funcs);
+ vc4_crtc->async_flip_wq = drmm_alloc_ordered_workqueue(drm, "%s-async-flip",
+ 0, crtc->name);
+ if (IS_ERR(vc4_crtc->async_flip_wq))
+ return PTR_ERR(vc4_crtc->async_flip_wq);
+
if (vc4->gen == VC4_GEN_4) {
drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 09134809bf15..f68486be08a6 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -596,6 +596,12 @@ struct vc4_crtc {
* access to that value.
*/
unsigned int current_hvs_channel;
+
+ /**
+ * @async_flip_wq: Ordered workqueue completing the CRTC's async
+ * page flips.
+ */
+ struct workqueue_struct *async_flip_wq;
};
#define to_vc4_crtc(_crtc) \
--
2.56.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v2 2/3] drm/vc4: Complete async page flips from an ordered workqueue
2026-11-14 19:16 ` [PATCH v2 2/3] drm/vc4: Complete async page flips from an ordered workqueue Maíra Canal
@ 2026-11-14 19:40 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-14 19:40 UTC (permalink / raw)
To: Maíra Canal; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] drm/vc4: inline flip in vc4_async_set_fence_cb() races with
pending flips
--
commit 6426f110104db1e4dcc5dd10cee848879c7ef9a8
Author: Maíra Canal <mcanal@igalia.com>
drm/vc4: Complete async page flips from an ordered workqueue
This commit defers async page flip completion from the dma_fence callback
to a per-CRTC ordered workqueue so completion work that may sleep does not
run in interrupt context.
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index c558b527c7e60..177ee27ab6d15 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
[ ... ]
> @@ -930,17 +933,19 @@ vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
> drm_framebuffer_put(flip_state->old_fb);
> }
>
> + dma_fence_put(flip_state->fence);
> kfree(flip_state);
> }
>
> -static void vc4_async_page_flip_fence_complete(struct dma_fence *fence,
> - struct dma_fence_cb *cb)
> +static void vc4_async_page_flip_fence_cb(struct dma_fence *fence,
> + struct dma_fence_cb *cb)
> {
> struct vc4_async_flip_state *flip_state =
> container_of(cb, struct vc4_async_flip_state, cb);
> + struct vc4_crtc *vc4_crtc = to_vc4_crtc(flip_state->crtc);
>
> - vc4_async_page_flip_complete(flip_state);
> - dma_fence_put(fence);
> + /* Completing the flip may sleep, so defer it to a worker. */
> + queue_work(vc4_crtc->async_flip_wq, &flip_state->work);
> }
>
> static int vc4_async_set_fence_cb(struct drm_device *dev,
> @@ -948,23 +953,26 @@ static int vc4_async_set_fence_cb(struct drm_device *dev,
> {
> struct drm_framebuffer *fb = flip_state->fb;
> struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0);
> - struct dma_fence *fence;
> + struct vc4_crtc *vc4_crtc = to_vc4_crtc(flip_state->crtc);
> int ret;
>
> - ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ, &fence);
> + INIT_WORK(&flip_state->work, vc4_async_page_flip_work);
> +
> + ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ,
> + &flip_state->fence);
> if (ret)
> return ret;
>
> - /* If there's no fence, complete the page flip immediately */
> - if (!fence) {
> - vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
> + if (flip_state->fence &&
> + !dma_fence_add_callback(flip_state->fence, &flip_state->cb,
> + vc4_async_page_flip_fence_cb))
> return 0;
> - }
>
> - /* If the fence has already been completed, complete the page flip */
> - if (dma_fence_add_callback(fence, &flip_state->cb,
> - vc4_async_page_flip_fence_complete))
> - vc4_async_page_flip_fence_complete(fence, &flip_state->cb);
> + /* No fence or the fence has already signaled. Complete the flip
> + * immediately, after earlier queued flips.
> + */
> + flush_workqueue(vc4_crtc->async_flip_wq);
> + vc4_async_page_flip_work(&flip_state->work);
[Severity: High]
Can an inline vc4_async_page_flip_work() call here race with or overtake
an earlier async flip on the same CRTC?
flush_workqueue() only waits for work items already queued on
vc4_crtc->async_flip_wq. If an earlier flip (flip1, to fb1) registered
vc4_async_page_flip_fence_cb() on an unsignaled fence, flip1->work is not
queued until that fence signals. If a second async flip (flip2, to fb2)
on the same CRTC has no fence or an already-signaled fence:
CPU0 CPU1 (irq / kworker)
vc4_async_page_flip_common() [flip1]
flip1->old_fb = fb0
plane->state->fb = fb1
dma_fence_add_callback(fence1, ...)
vc4_async_page_flip_common() [flip2]
flip2->old_fb = fb1
plane->state->fb = fb2
flush_workqueue(async_flip_wq)
vc4_async_page_flip_work(&flip2->work)
vc4_plane_async_set_fb(plane, fb2)
vc4_bo_dec_usecnt(fb1)
drm_framebuffer_put(fb1)
dma_fence_signal(fence1)
queue_work(..., &flip1->work)
vc4_async_page_flip_work(&flip1->work)
vc4_plane_async_set_fb(plane, fb1)
drm_framebuffer_put(fb1)
<-- HVS scans out unreferenced fb1
flip2 completes before flip1 and drops the old_fb reference and BO usecnt
for fb1. When flip1->work later runs, it overwrites the HVS scanout
address back to fb1 and drops flip1->fb, leaving the HVS scanning out fb1
after its flip references and BO usecnt have been released.
If fence1 signals while flip2 is past flush_workqueue(), both
vc4_async_page_flip_work() instances also run concurrently on the same
plane and update vc4_state->hw_dlist and vc4_state->dlist in
vc4_plane_async_set_fb() without locking.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-vc4-async-flip-fixes-v2-0-d33cbe99ed5d@igalia.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] drm/vc4: Look up the async page flip fence before committing the flip
2026-11-14 19:16 [PATCH v2 0/3] drm/vc4: Fix async page flip completion Maíra Canal
2026-11-14 19:16 ` [PATCH v2 1/3] drm/vc4: Drop BO usecnt before releasing the old FB Maíra Canal
2026-11-14 19:16 ` [PATCH v2 2/3] drm/vc4: Complete async page flips from an ordered workqueue Maíra Canal
@ 2026-11-14 19:16 ` Maíra Canal
2 siblings, 0 replies; 5+ messages in thread
From: Maíra Canal @ 2026-11-14 19:16 UTC (permalink / raw)
To: Iago Toral, Melissa Wen, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel-dev, dri-devel, stable, Maíra Canal
vc4_async_page_flip_common() ignores dma_resv_get_singleton() failures.
By then the plane state already points at the new framebuffer, so the
flip is never completed. So, its state leaks, the vblank reference is
never dropped and the event is never sent.
Look up the fence before changing any state, so the error can be properly
returned to userspace.
Cc: stable@vger.kernel.org
Fixes: d19e00ee06a9 ("drm/vc4: crtc: Fix out of order frames during asynchronous page flips")
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 177ee27ab6d1..4b6d4a5f7bc9 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -948,33 +948,22 @@ static void vc4_async_page_flip_fence_cb(struct dma_fence *fence,
queue_work(vc4_crtc->async_flip_wq, &flip_state->work);
}
-static int vc4_async_set_fence_cb(struct drm_device *dev,
- struct vc4_async_flip_state *flip_state)
+static void vc4_async_set_fence_cb(struct vc4_async_flip_state *flip_state)
{
- struct drm_framebuffer *fb = flip_state->fb;
- struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(flip_state->crtc);
- int ret;
INIT_WORK(&flip_state->work, vc4_async_page_flip_work);
- ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ,
- &flip_state->fence);
- if (ret)
- return ret;
-
if (flip_state->fence &&
!dma_fence_add_callback(flip_state->fence, &flip_state->cb,
vc4_async_page_flip_fence_cb))
- return 0;
+ return;
/* No fence or the fence has already signaled. Complete the flip
* immediately, after earlier queued flips.
*/
flush_workqueue(vc4_crtc->async_flip_wq);
vc4_async_page_flip_work(&flip_state->work);
-
- return 0;
}
static int
@@ -983,14 +972,22 @@ vc4_async_page_flip_common(struct drm_crtc *crtc,
struct drm_pending_vblank_event *event,
uint32_t flags)
{
- struct drm_device *dev = crtc->dev;
+ struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0);
struct drm_plane *plane = crtc->primary;
struct vc4_async_flip_state *flip_state;
+ int ret;
flip_state = kzalloc_obj(*flip_state);
if (!flip_state)
return -ENOMEM;
+ ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ,
+ &flip_state->fence);
+ if (ret) {
+ kfree(flip_state);
+ return ret;
+ }
+
drm_framebuffer_get(fb);
flip_state->fb = fb;
flip_state->crtc = crtc;
@@ -1016,7 +1013,7 @@ vc4_async_page_flip_common(struct drm_crtc *crtc,
*/
drm_atomic_set_fb_for_plane(plane->state, fb);
- vc4_async_set_fence_cb(dev, flip_state);
+ vc4_async_set_fence_cb(flip_state);
/* Driver takes ownership of state on successful async commit. */
return 0;
--
2.56.0
^ permalink raw reply related [flat|nested] 5+ messages in thread