5e68aec2e9
Replace the hand-rolled draft patches with the proper git-format-patch output. The new files apply cleanly via git am against unmodified Linux 6.12 mainline, verified by reset-and-apply roundtrip on /tmp/hantro-src (the local sparse checkout used during the chromium-fourier campaign). All kernel API calls also sanity-checked against the real include/linux/dma-fence.h and include/linux/dma-resv.h signatures: - dma_fence_init(fence, ops, lock, context, seqno) — argument list matches our call exactly - dma_resv_add_fence(obj, fence, usage) — DMA_RESV_USAGE_WRITE enum value confirmed present - dma_fence_signal, dma_fence_set_error, dma_fence_get, dma_fence_put, dma_fence_context_alloc — all present and correctly used - dma_resv_lock(obj, NULL), dma_resv_unlock — present, correctly paired README updated to reflect the post-verification status. Remaining gates before sending to linux-media are now: full-tree compile test (needs complete kernel checkout, hours of work), boot test on ohm (needs patched kernel build), and the runtime A/B (install patched kernel + uninstall kwin-fourier — chrome should still play 1080p30 because the fence is now real). Cover letter blurb filled in with the full motivation, test setup, and review-question list.
225 lines
7.5 KiB
Diff
225 lines
7.5 KiB
Diff
From 1f7a526331061ad767b2eb8401b0d28984888ae6 Mon Sep 17 00:00:00 2001
|
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
|
Date: Tue, 28 Apr 2026 19:23:50 +0000
|
|
Subject: [PATCH 1/3] media: videobuf2: add dma_resv release-fence helper
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Add an opt-in API that lets vb2 producers populate a dma_resv
|
|
exclusive write fence on the dmabufs they export to userspace,
|
|
signalled when the buffer transitions to VB2_BUF_STATE_DONE.
|
|
|
|
V4L2 producers historically don't propagate buffer-state-done into
|
|
the dmabuf's dma_resv exclusive fence. Userspace consumers that
|
|
import V4L2-produced dmabufs and wait on the dmabuf's implicit-sync
|
|
fence (poll(POLLIN), DMA_BUF_IOCTL_EXPORT_SYNC_FILE) currently see
|
|
either zero fences or a stub fence from dma_fence_get_stub(). This
|
|
is correct by accident for the common case (clients call DQBUF
|
|
before importing) but represents a contract gap.
|
|
|
|
Drivers opt in by calling vb2_buffer_attach_release_fence(vb) from
|
|
their buf_queue callback. The helper allocates a dma_fence on the
|
|
queue's fence context (set up at vb2_core_queue_init), attaches it
|
|
as DMA_RESV_USAGE_WRITE on each plane's dmabuf->resv, and stashes
|
|
it in vb->release_fence. vb2_buffer_done signals + puts the fence
|
|
as part of its state transition.
|
|
|
|
For drivers that don't opt in, vb->release_fence stays NULL and
|
|
the signal path is a no-op.
|
|
|
|
Skips planes whose vb2_plane.dbuf is NULL — buffers never exported
|
|
via VIDIOC_EXPBUF (or imported via V4L2_MEMORY_DMABUF) have no
|
|
dmabuf for userspace to wait on.
|
|
|
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
|
---
|
|
.../media/common/videobuf2/videobuf2-core.c | 95 +++++++++++++++++++
|
|
include/media/videobuf2-core.h | 29 ++++++
|
|
2 files changed, 124 insertions(+)
|
|
|
|
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
|
|
index b0523fc23..ee766aae0 100644
|
|
--- a/drivers/media/common/videobuf2/videobuf2-core.c
|
|
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
|
|
@@ -26,6 +26,9 @@
|
|
#include <linux/freezer.h>
|
|
#include <linux/kthread.h>
|
|
|
|
+#include <linux/dma-fence.h>
|
|
+#include <linux/dma-resv.h>
|
|
+#include <linux/dma-buf.h>
|
|
#include <media/videobuf2-core.h>
|
|
#include <media/v4l2-mc.h>
|
|
|
|
@@ -1179,6 +1182,86 @@ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
|
|
}
|
|
EXPORT_SYMBOL_GPL(vb2_plane_cookie);
|
|
|
|
+/*
|
|
+ * dma_resv release-fence integration.
|
|
+ *
|
|
+ * V4L2 producers historically don't propagate buffer-state-done into
|
|
+ * the dmabuf's dma_resv exclusive fence. Userspace consumers that
|
|
+ * wait on that fence (e.g. wayland compositors via poll(POLLIN) or
|
|
+ * DMA_BUF_IOCTL_EXPORT_SYNC_FILE) currently see either no fences or
|
|
+ * a stub fence from dma_fence_get_stub(). The opt-in API below lets
|
|
+ * a driver attach a real producer fence at QBUF time and have it
|
|
+ * signalled by vb2_buffer_done().
|
|
+ */
|
|
+
|
|
+static const char *vb2_dma_resv_get_driver_name(struct dma_fence *fence)
|
|
+{
|
|
+ return "videobuf2";
|
|
+}
|
|
+
|
|
+static const char *vb2_dma_resv_get_timeline_name(struct dma_fence *fence)
|
|
+{
|
|
+ return "vb2-release-fence";
|
|
+}
|
|
+
|
|
+static const struct dma_fence_ops vb2_dma_resv_fence_ops = {
|
|
+ .get_driver_name = vb2_dma_resv_get_driver_name,
|
|
+ .get_timeline_name = vb2_dma_resv_get_timeline_name,
|
|
+};
|
|
+
|
|
+int vb2_buffer_attach_release_fence(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct vb2_queue *q = vb->vb2_queue;
|
|
+ struct dma_fence *fence;
|
|
+ unsigned int plane;
|
|
+
|
|
+ if (WARN_ON(vb->release_fence))
|
|
+ return -EINVAL;
|
|
+
|
|
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
|
+ if (!fence)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ dma_fence_init(fence, &vb2_dma_resv_fence_ops, &q->dma_resv_fence_lock,
|
|
+ q->dma_resv_fence_context,
|
|
+ atomic64_inc_return(&q->dma_resv_fence_seqno));
|
|
+
|
|
+ for (plane = 0; plane < vb->num_planes; plane++) {
|
|
+ struct dma_buf *dbuf = vb->planes[plane].dbuf;
|
|
+
|
|
+ if (!dbuf)
|
|
+ continue;
|
|
+
|
|
+ dma_resv_lock(dbuf->resv, NULL);
|
|
+ dma_resv_add_fence(dbuf->resv, fence, DMA_RESV_USAGE_WRITE);
|
|
+ dma_resv_unlock(dbuf->resv);
|
|
+ }
|
|
+
|
|
+ /* One reference for the eventual signal in vb2_buffer_done. */
|
|
+ vb->release_fence = dma_fence_get(fence);
|
|
+
|
|
+ /* The dma_resv held its own reference per plane. Drop ours. */
|
|
+ dma_fence_put(fence);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(vb2_buffer_attach_release_fence);
|
|
+
|
|
+static void vb2_buffer_signal_release_fence(struct vb2_buffer *vb,
|
|
+ enum vb2_buffer_state state)
|
|
+{
|
|
+ struct dma_fence *fence = vb->release_fence;
|
|
+
|
|
+ if (!fence)
|
|
+ return;
|
|
+
|
|
+ if (state == VB2_BUF_STATE_ERROR)
|
|
+ dma_fence_set_error(fence, -EIO);
|
|
+ dma_fence_signal(fence);
|
|
+ dma_fence_put(fence);
|
|
+ vb->release_fence = NULL;
|
|
+}
|
|
+
|
|
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
|
|
{
|
|
struct vb2_queue *q = vb->vb2_queue;
|
|
@@ -1205,6 +1288,9 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
|
|
if (state != VB2_BUF_STATE_QUEUED)
|
|
__vb2_buf_mem_finish(vb);
|
|
|
|
+ if (state != VB2_BUF_STATE_QUEUED)
|
|
+ vb2_buffer_signal_release_fence(vb, state);
|
|
+
|
|
spin_lock_irqsave(&q->done_lock, flags);
|
|
if (state == VB2_BUF_STATE_QUEUED) {
|
|
vb->state = VB2_BUF_STATE_QUEUED;
|
|
@@ -2652,6 +2738,15 @@ int vb2_core_queue_init(struct vb2_queue *q)
|
|
mutex_init(&q->mmap_lock);
|
|
init_waitqueue_head(&q->done_wq);
|
|
|
|
+ /*
|
|
+ * Per-queue dma_resv release-fence context. Drivers opt-in via
|
|
+ * vb2_buffer_attach_release_fence(); other drivers pay only the
|
|
+ * cost of the unused fields.
|
|
+ */
|
|
+ q->dma_resv_fence_context = dma_fence_context_alloc(1);
|
|
+ atomic64_set(&q->dma_resv_fence_seqno, 0);
|
|
+ spin_lock_init(&q->dma_resv_fence_lock);
|
|
+
|
|
q->memory = VB2_MEMORY_UNKNOWN;
|
|
|
|
if (q->buf_struct_size == 0)
|
|
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
|
|
index 9b02aeba4..2bf3272d4 100644
|
|
--- a/include/media/videobuf2-core.h
|
|
+++ b/include/media/videobuf2-core.h
|
|
@@ -288,6 +288,12 @@ struct vb2_buffer {
|
|
unsigned int skip_cache_sync_on_finish:1;
|
|
|
|
struct vb2_plane planes[VB2_MAX_PLANES];
|
|
+ /*
|
|
+ * dma_resv release fence — set by vb2_buffer_attach_release_fence()
|
|
+ * (driver opt-in from buf_queue), signalled and put by
|
|
+ * vb2_buffer_done(). NULL for drivers that don't opt in.
|
|
+ */
|
|
+ struct dma_fence *release_fence;
|
|
struct list_head queued_entry;
|
|
struct list_head done_entry;
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
@@ -658,6 +664,15 @@ struct vb2_queue {
|
|
spinlock_t done_lock;
|
|
wait_queue_head_t done_wq;
|
|
|
|
+ /*
|
|
+ * Per-queue dma_resv release-fence context. Drivers that opt
|
|
+ * into vb2_buffer_attach_release_fence() use these to allocate
|
|
+ * fences on a single per-queue timeline.
|
|
+ */
|
|
+ u64 dma_resv_fence_context;
|
|
+ atomic64_t dma_resv_fence_seqno;
|
|
+ spinlock_t dma_resv_fence_lock;
|
|
+
|
|
unsigned int streaming:1;
|
|
unsigned int start_streaming_called:1;
|
|
unsigned int error:1;
|
|
@@ -747,6 +762,20 @@ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no);
|
|
*/
|
|
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
|
|
|
|
+/**
|
|
+ * vb2_buffer_attach_release_fence() - opt-in dma_resv release fence.
|
|
+ * @vb: the buffer being queued to the producer.
|
|
+ *
|
|
+ * Drivers call this from their buf_queue callback to attach an
|
|
+ * exclusive write fence to each plane's dmabuf->resv. The fence
|
|
+ * is signalled and put by vb2_buffer_done() when the buffer
|
|
+ * transitions to VB2_BUF_STATE_DONE / _ERROR. Skips planes whose
|
|
+ * dbuf is NULL.
|
|
+ *
|
|
+ * Returns 0 on success, negative errno on allocation failure.
|
|
+ */
|
|
+int vb2_buffer_attach_release_fence(struct vb2_buffer *vb);
|
|
+
|
|
/**
|
|
* vb2_discard_done() - discard all buffers marked as DONE.
|
|
* @q: pointer to &struct vb2_queue with videobuf2 queue.
|
|
--
|
|
2.47.3
|
|
|