[RFC 1/8] dma-buf/fence: add fence_collection fences

Gustavo Padovan gustavo at padovan.org
Fri Apr 15 18:27:50 UTC 2016


2016-04-15 Christian König <christian.koenig at amd.com>:

> Am 15.04.2016 um 10:02 schrieb Daniel Vetter:
> >On Thu, Apr 14, 2016 at 06:29:34PM -0700, Gustavo Padovan wrote:
> >>From: Gustavo Padovan <gustavo.padovan at collabora.co.uk>
> >>
> >>struct fence_collection inherits from struct fence and carries a
> >>collection of fences that needs to be waited together.
> >>
> >>It is useful to translate a sync_file to a fence to remove the complexity
> >>of dealing with sync_files from DRM drivers. So even if there are many
> >>fences in the sync_file that needs to waited for a commit to happen
> >>drivers would only worry about a standard struct fence.That means that no
> >>changes needed to any driver besides supporting fences.
> >>
> >>fence_collection's fence doesn't belong to any timeline context.
> >>
> >>Signed-off-by: Gustavo Padovan <gustavo.padovan at collabora.co.uk>
> >>---
> >>  drivers/dma-buf/Makefile           |   2 +-
> >>  drivers/dma-buf/fence-collection.c | 138 +++++++++++++++++++++++++++++++++++++
> >>  drivers/dma-buf/fence.c            |   2 +-
> >>  include/linux/fence-collection.h   |  56 +++++++++++++++
> >>  include/linux/fence.h              |   2 +
> >>  5 files changed, 198 insertions(+), 2 deletions(-)
> >>  create mode 100644 drivers/dma-buf/fence-collection.c
> >>  create mode 100644 include/linux/fence-collection.h
> >>
> >>diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> >>index 43325a1..30b8464 100644
> >>--- a/drivers/dma-buf/Makefile
> >>+++ b/drivers/dma-buf/Makefile
> >>@@ -1,3 +1,3 @@
> >>-obj-y := dma-buf.o fence.o reservation.o seqno-fence.o
> >>+obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-collection.o
> >>  obj-$(CONFIG_SYNC_FILE)		+= sync_file.o sync_timeline.o sync_debug.o
> >>  obj-$(CONFIG_SW_SYNC)		+= sw_sync.o
> >>diff --git a/drivers/dma-buf/fence-collection.c b/drivers/dma-buf/fence-collection.c
> >>new file mode 100644
> >>index 0000000..8a4ecb0
> >>--- /dev/null
> >>+++ b/drivers/dma-buf/fence-collection.c
> >>@@ -0,0 +1,138 @@
> >>+/*
> >>+ * fence-collection: aggregate fences to be waited together
> >>+ *
> >>+ * Copyright (C) 2016 Collabora Ltd
> >>+ * Authors:
> >>+ *	Gustavo Padovan <gustavo at padovan.org>
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or modify it
> >>+ * under the terms of the GNU General Public License version 2 as published by
> >>+ * the Free Software Foundation.
> >>+ *
> >>+ * This program is distributed in the hope that it will be useful, but WITHOUT
> >>+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> >>+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> >>+ * more details.
> >>+ */
> >>+
> >>+#include <linux/export.h>
> >>+#include <linux/slab.h>
> >>+#include <linux/fence-collection.h>
> >>+
> >>+static const char *fence_collection_get_driver_name(struct fence *fence)
> >>+{
> >>+	struct fence_collection *collection = to_fence_collection(fence);
> >>+	struct fence *f = collection->fences[0].fence;
> >>+
> >>+	return f->ops->get_driver_name(fence);
> >>+}
> 
> I would rather return some constant name here instead of relying that the
> collection already has a fence added and that all fences are from the same
> driver.

If we merge _init and _add this will not be a problem anymore and we can
return the actual driver name.

> 
> >>+
> >>+static const char *fence_collection_get_timeline_name(struct fence *fence)
> >>+{
> >>+	return "no context";
> >>+}
> >>+
> >>+static bool fence_collection_enable_signaling(struct fence *fence)
> >>+{
> >>+	struct fence_collection *collection = to_fence_collection(fence);
> >>+
> >>+	return atomic_read(&collection->num_pending_fences);
> >>+}
> >>+
> >>+static bool fence_collection_signaled(struct fence *fence)
> >>+{
> >>+	struct fence_collection *collection = to_fence_collection(fence);
> >>+
> >>+	return (atomic_read(&collection->num_pending_fences) == 0);
> >>+}
> >>+
> >>+static void fence_collection_release(struct fence *fence)
> >>+{
> >>+	struct fence_collection *collection = to_fence_collection(fence);
> >>+	int i;
> >>+
> >>+	for (i = 0 ; i < collection->num_fences ; i++) {
> >>+		fence_remove_callback(collection->fences[i].fence,
> >>+				      &collection->fences[i].cb);
> >>+		fence_put(collection->fences[i].fence);
> >>+	}
> >>+
> >>+	fence_free(fence);
> >>+}
> >>+
> >>+static signed long fence_collection_wait(struct fence *fence, bool intr,
> >>+					 signed long timeout)
> >>+{
> >>+	struct fence_collection *collection = to_fence_collection(fence);
> >>+	int i;
> >>+
> >>+	for (i = 0 ; i < collection->num_fences ; i++) {
> >>+		timeout = fence_wait(collection->fences[i].fence, intr);
> >>+		if (timeout < 0)
> >>+			return timeout;
> >>+	}
> >>+
> >>+	return timeout;
> >>+}
> >>+
> >>+static const struct fence_ops fence_collection_ops = {
> >>+	.get_driver_name = fence_collection_get_driver_name,
> >>+	.get_timeline_name = fence_collection_get_timeline_name,
> >>+	.enable_signaling = fence_collection_enable_signaling,
> >>+	.signaled = fence_collection_signaled,
> >>+	.wait = fence_collection_wait,
> >>+	.release = fence_collection_release,
> >>+};
> >>+
> >>+static void collection_check_cb_func(struct fence *fence, struct fence_cb *cb)
> >>+{
> >>+	struct fence_collection_cb *f_cb;
> >>+	struct fence_collection *collection;
> >>+
> >>+	f_cb = container_of(cb, struct fence_collection_cb, cb);
> >>+	collection = f_cb->collection;
> >>+
> >>+	if (atomic_dec_and_test(&collection->num_pending_fences))
> >>+		fence_signal(&collection->base);
> >>+}
> >>+
> >>+void fence_collection_add(struct fence_collection *collection,
> >>+			  struct fence *fence)
> >>+{
> >>+	int n = collection->num_fences;
> >>+
> >>+	collection->fences[n].collection = collection;
> >>+	collection->fences[n].fence = fence;
> >>+
> >>+	if (fence_add_callback(fence, &collection->fences[n].cb,
> >>+				 collection_check_cb_func))
> >>+		return;
> >>+
> >>+	fence_get(fence);
> >>+
> >>+	collection->num_fences++;
> >>+	atomic_inc(&collection->num_pending_fences);
> >>+}
> >For the interface I think we should not split it into _init and _add - it
> >shouldn't be allowed to change a collection once it's created. So probably
> >cleaner if we add an array of fence pointers to _init.
> 
> Amdgpu also has an implementation for a fence collection which uses a a
> hashtable to keep the fences grouped by context (e.g. only the latest fence
> is keept for each context). See amdgpu_sync.c for reference.
> 
> We should either make the collection similar in a way that you can add as
> many fences as you want (like the amdgpu implementation) or make it static
> and only add a fixed number of fences right from the beginning.
> 
> I can certainly see use cases for both, but if you want to stick with a
> static approach you should probably call the new object fence_array instead
> of fence_collection and do as Daniel suggested.

Maybe we can go for something in between. Have fence_collection_init()
need at least two fences to create the fence_collection. Then
fence_collection_add() would add more dinamically.

> 
> >Other nitpick: Adding the callback should (I think) only be done in
> >->enable_signalling.
> 
> Yeah, I was about to complain on that as well.
> 
> Enabling signaling can have a huge overhead for some fence implementations.
> So it should only be used when needed

Right, that makes sense.

> 
> >
> >Finally: Have you looked into stitching together a few unit tests for
> >fence_collection?

Not yet. It is something I plan to work soon.

> >
> >Fence collections also break the assumption that every fence is on a
> >timeline. fence_later and fence_is_later need to be adjusted. We also need
> >a special collection context to filter these out. This means
> >fence_collection isn't perfectly opaque abstraction.

I'll fix this.

> >>+
> >>+struct fence_collection *fence_collection_init(int num_fences)
> >>+{
> >>+	struct fence_collection *collection;
> >>+
> >>+	collection = kzalloc(offsetof(struct fence_collection,
> >>+				      fences[num_fences]), GFP_KERNEL);
> >>+	if (!collection)
> >>+		return NULL;
> >>+
> >>+	spin_lock_init(&collection->lock);
> >>+	fence_init(&collection->base, &fence_collection_ops, &collection->lock,
> >>+		   FENCE_NO_CONTEXT, 0);
> >>+
> >>+	return collection;
> >>+}
> >>+EXPORT_SYMBOL(fence_collection_init);
> >>+
> >>+void fence_collection_put(struct fence_collection *collection)
> >>+{
> >>+	fence_put(&collection->base);
> >Not sure a specialized _put function is useful, I'd leave it out.

I've added this to let the user only deal with fence_collection, but 
I'm fine leaving it out too.

	Gustavo


More information about the dri-devel mailing list