diff mbox

[8/9] dma-buf/dma-fence: Add a mechanism for proxy fences

Message ID 1502491174-10913-9-git-send-email-jason.ekstrand@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jason Ekstrand Aug. 11, 2017, 10:39 p.m. UTC
From: Chris Wilson <chris@chris-wilson.co.uk>

Proxy fences allow you to create a place-holder fence which you can
later assign to the "real" fence at which point the two look
indistinguishable to anyone other than the driver which created the
real fence.  These should be used with care as it is the responsibility
of proxy fences creator to ensure that it eventually gets assigned a
real fence so it gets signaled.

Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
---
 drivers/dma-buf/Makefile          |   4 +-
 drivers/dma-buf/dma-fence-proxy.c | 186 ++++++++++++++++++++++++++++++++++++++
 include/linux/dma-fence-proxy.h   |  25 +++++
 3 files changed, 214 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma-buf/dma-fence-proxy.c
 create mode 100644 include/linux/dma-fence-proxy.h
diff mbox

Patch

diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index c33bf88..e468215 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,3 +1,5 @@ 
-obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o
+obj-y := dma-buf.o \
+	dma-fence.o dma-fence-array.o dma-fence-proxy.o \
+	reservation.o seqno-fence.o
 obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
 obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
diff --git a/drivers/dma-buf/dma-fence-proxy.c b/drivers/dma-buf/dma-fence-proxy.c
new file mode 100644
index 0000000..61bf8e5
--- /dev/null
+++ b/drivers/dma-buf/dma-fence-proxy.c
@@ -0,0 +1,186 @@ 
+/*
+ * dma-fence-proxy: placeholder unsignaled fence
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * 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/dma-fence.h>
+#include <linux/export.h>
+#include <linux/irq_work.h>
+#include <linux/slab.h>
+
+struct dma_fence_proxy {
+	struct dma_fence base;
+	spinlock_t lock;
+
+	const char *driver_name;
+	void *tag;
+
+	struct dma_fence *real;
+	struct dma_fence_cb cb;
+	struct irq_work work;
+};
+
+static const char *proxy_get_driver_name(struct dma_fence *fence)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	return p->real ? p->real->ops->get_driver_name(p->real) : p->driver_name;
+}
+
+static const char *proxy_get_timeline_name(struct dma_fence *fence)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	return p->real ? p->real->ops->get_timeline_name(p->real) : "unset";
+}
+
+static void proxy_irq_work(struct irq_work *work)
+{
+	struct dma_fence_proxy *p = container_of(work, typeof(*p), work);
+
+	dma_fence_signal(&p->base);
+	dma_fence_put(&p->base);
+}
+
+static void proxy_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
+{
+	struct dma_fence_proxy *p = container_of(cb, typeof(*p), cb);
+
+	/* beware the alleged spinlock inversion */
+	irq_work_queue(&p->work);
+}
+
+static bool proxy_enable_signaling(struct dma_fence *fence)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	if (!p->real)
+		return true;
+
+	if (dma_fence_add_callback(p->real, &p->cb, proxy_callback))
+		return false;
+
+	dma_fence_get(fence);
+	return true;
+}
+
+static bool proxy_signaled(struct dma_fence *fence)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	return p->real ? dma_fence_is_signaled(p->real) : false;
+}
+
+static void proxy_release(struct dma_fence *fence)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	if (!p->real)
+		dma_fence_signal(&p->base);
+
+	dma_fence_put(p->real);
+	dma_fence_free(&p->base);
+}
+
+static const struct dma_fence_ops dma_fence_proxy_ops = {
+	.get_driver_name = proxy_get_driver_name,
+	.get_timeline_name = proxy_get_timeline_name,
+	.enable_signaling = proxy_enable_signaling,
+	.signaled = proxy_signaled,
+	.wait = dma_fence_default_wait,
+	.release = proxy_release,
+};
+
+/**
+ * dma_fence_proy_create - Create an unset proxy dma-fence
+ * @driver_name: The driver name to report; must outlive the fence
+ * @tag: A pointer which uniquely identifies the creator
+ */
+struct dma_fence *dma_fence_create_proxy(const char *driver_name, void *tag)
+{
+	struct dma_fence_proxy *p;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return NULL;
+
+	p->driver_name = driver_name;
+	p->tag = tag;
+	spin_lock_init(&p->lock);
+	dma_fence_init(&p->base, &dma_fence_proxy_ops, &p->lock, 0, 0);
+	init_irq_work(&p->work, proxy_irq_work);
+
+	return &p->base;
+}
+EXPORT_SYMBOL(dma_fence_create_proxy);
+
+static bool dma_fence_is_proxy(struct dma_fence *fence)
+{
+	return fence->ops == &dma_fence_proxy_ops;
+}
+
+/**
+ * dma_fence_is_proxy_tagged - identify a proxy fence
+ * @fence: The fence to identify
+ * @tag: The tag pointer provided to dma_fence_create_proxy
+ *
+ * This returns true if this is a proxy fence tag is the same pointer as
+ * the tag provided to dma_fence_create_proxy.
+ */
+bool dma_fence_is_proxy_tagged(struct dma_fence *fence, void *tag)
+{
+	struct dma_fence_proxy *p = container_of(fence, typeof(*p), base);
+
+	if (!dma_fence_is_proxy(fence))
+		return false;
+
+	return p->tag == tag;
+}
+EXPORT_SYMBOL(dma_fence_is_proxy_tagged);
+
+/**
+ * dma_fence_proxy_assign - assign a fence to a proxy fence
+ * @proxy: The proxy fence
+ * @real: The real fence to assign to proxy
+ *
+ * This assigns the given real fence to the proxy fence.  From this point
+ * forward, the proxy fence will be almost indistinguishable from the real
+ * fence.  It will report the same driver and timeline names and will
+ * signal when the real fence signals.  If the real fence is already
+ * signaled when this function is called, it will signal as soon as it has
+ * any listeners, possibly immediately.
+ */
+void dma_fence_proxy_assign(struct dma_fence *proxy, struct dma_fence *real)
+{
+	struct dma_fence_proxy *p = container_of(proxy, typeof(*p), base);
+	unsigned long flags;
+
+	BUG_ON(!dma_fence_is_proxy(proxy));
+	BUG_ON(p->real);
+
+	spin_lock_irqsave(p->base.lock, flags);
+
+	p->real = dma_fence_get(real);
+
+	if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &p->base.flags)) {
+		if (dma_fence_add_callback(real, &p->cb, proxy_callback))
+			dma_fence_signal_locked(&p->base);
+		else
+			dma_fence_get(&p->base);
+	} else if (dma_fence_is_signaled(real)) {
+		dma_fence_signal_locked(&p->base);
+	}
+
+	spin_unlock_irqrestore(p->base.lock, flags);
+}
+EXPORT_SYMBOL(dma_fence_proxy_assign);
diff --git a/include/linux/dma-fence-proxy.h b/include/linux/dma-fence-proxy.h
new file mode 100644
index 0000000..177a5165
--- /dev/null
+++ b/include/linux/dma-fence-proxy.h
@@ -0,0 +1,25 @@ 
+/*
+ * dma-fence-proxy: allows waiting upon unset fences
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_DMA_FENCE_PROXY_H
+#define __LINUX_DMA_FENCE_PROXY_H
+
+#include <linux/dma-fence.h>
+
+struct dma_fence *dma_fence_create_proxy(const char *driver_name, void *tag);
+bool dma_fence_is_proxy_tagged(struct dma_fence *fence, void *tag);
+void dma_fence_proxy_assign(struct dma_fence *proxy, struct dma_fence *real);
+
+#endif /* __LINUX_DMA_FENCE_PROXY_H */