Merge drm-misc/drm-misc-next-fixes into drm-misc-fixes
authorMaxime Ripard <maxime@cerno.tech>
Tue, 5 Apr 2022 09:38:33 +0000 (11:38 +0200)
committerMaxime Ripard <maxime@cerno.tech>
Tue, 5 Apr 2022 09:39:22 +0000 (11:39 +0200)
There were a few patches left in drm-misc-next-fixes, let's bring them
into drm-misc-fixes.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Documentation/driver-api/dma-buf.rst
drivers/dma-buf/Makefile
drivers/dma-buf/dma-fence-array.c
drivers/dma-buf/selftests.h
drivers/dma-buf/st-dma-fence-unwrap.c [new file with mode: 0644]
drivers/dma-buf/sync_file.c
drivers/gpu/drm/drm_of.c
include/linux/dma-fence-array.h
include/linux/dma-fence-chain.h
include/linux/dma-fence-unwrap.h [new file with mode: 0644]

index 5500667..36a76cb 100644 (file)
@@ -185,6 +185,12 @@ DMA Fence Chain
 .. kernel-doc:: include/linux/dma-fence-chain.h
    :internal:
 
+DMA Fence unwrap
+~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: include/linux/dma-fence-unwrap.h
+   :internal:
+
 DMA Fence uABI/Sync File
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
index 511805d..4c9eb53 100644 (file)
@@ -12,6 +12,7 @@ dmabuf_selftests-y := \
        selftest.o \
        st-dma-fence.o \
        st-dma-fence-chain.o \
+       st-dma-fence-unwrap.o \
        st-dma-resv.o
 
 obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
index cb1bacb..5c8a708 100644 (file)
@@ -159,6 +159,8 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
        struct dma_fence_array *array;
        size_t size = sizeof(*array);
 
+       WARN_ON(!num_fences || !fences);
+
        /* Allocate the callback structures behind the array. */
        size += num_fences * sizeof(struct dma_fence_array_cb);
        array = kzalloc(size, GFP_KERNEL);
@@ -219,3 +221,33 @@ bool dma_fence_match_context(struct dma_fence *fence, u64 context)
        return true;
 }
 EXPORT_SYMBOL(dma_fence_match_context);
+
+struct dma_fence *dma_fence_array_first(struct dma_fence *head)
+{
+       struct dma_fence_array *array;
+
+       if (!head)
+               return NULL;
+
+       array = to_dma_fence_array(head);
+       if (!array)
+               return head;
+
+       if (!array->num_fences)
+               return NULL;
+
+       return array->fences[0];
+}
+EXPORT_SYMBOL(dma_fence_array_first);
+
+struct dma_fence *dma_fence_array_next(struct dma_fence *head,
+                                      unsigned int index)
+{
+       struct dma_fence_array *array = to_dma_fence_array(head);
+
+       if (!array || index >= array->num_fences)
+               return NULL;
+
+       return array->fences[index];
+}
+EXPORT_SYMBOL(dma_fence_array_next);
index 97d73aa..8519658 100644 (file)
@@ -12,4 +12,5 @@
 selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
 selftest(dma_fence, dma_fence)
 selftest(dma_fence_chain, dma_fence_chain)
+selftest(dma_fence_unwrap, dma_fence_unwrap)
 selftest(dma_resv, dma_resv)
diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
new file mode 100644 (file)
index 0000000..039f016
--- /dev/null
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: MIT
+
+/*
+ * Copyright (C) 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/dma-fence-unwrap.h>
+#if 0
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mm.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#endif
+
+#include "selftest.h"
+
+#define CHAIN_SZ (4 << 10)
+
+static inline struct mock_fence {
+       struct dma_fence base;
+       spinlock_t lock;
+} *to_mock_fence(struct dma_fence *f) {
+       return container_of(f, struct mock_fence, base);
+}
+
+static const char *mock_name(struct dma_fence *f)
+{
+       return "mock";
+}
+
+static const struct dma_fence_ops mock_ops = {
+       .get_driver_name = mock_name,
+       .get_timeline_name = mock_name,
+};
+
+static struct dma_fence *mock_fence(void)
+{
+       struct mock_fence *f;
+
+       f = kmalloc(sizeof(*f), GFP_KERNEL);
+       if (!f)
+               return NULL;
+
+       spin_lock_init(&f->lock);
+       dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
+
+       return &f->base;
+}
+
+static struct dma_fence *mock_array(unsigned int num_fences, ...)
+{
+       struct dma_fence_array *array;
+       struct dma_fence **fences;
+       va_list valist;
+       int i;
+
+       fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
+       if (!fences)
+               return NULL;
+
+       va_start(valist, num_fences);
+       for (i = 0; i < num_fences; ++i)
+               fences[i] = va_arg(valist, typeof(*fences));
+       va_end(valist);
+
+       array = dma_fence_array_create(num_fences, fences,
+                                      dma_fence_context_alloc(1),
+                                      1, false);
+       if (!array)
+               goto cleanup;
+       return &array->base;
+
+cleanup:
+       for (i = 0; i < num_fences; ++i)
+               dma_fence_put(fences[i]);
+       kfree(fences);
+       return NULL;
+}
+
+static struct dma_fence *mock_chain(struct dma_fence *prev,
+                                   struct dma_fence *fence)
+{
+       struct dma_fence_chain *f;
+
+       f = dma_fence_chain_alloc();
+       if (!f) {
+               dma_fence_put(prev);
+               dma_fence_put(fence);
+               return NULL;
+       }
+
+       dma_fence_chain_init(f, prev, fence, 1);
+       return &f->base;
+}
+
+static int sanitycheck(void *arg)
+{
+       struct dma_fence *f, *chain, *array;
+       int err = 0;
+
+       f = mock_fence();
+       if (!f)
+               return -ENOMEM;
+
+       array = mock_array(1, f);
+       if (!array)
+               return -ENOMEM;
+
+       chain = mock_chain(NULL, array);
+       if (!chain)
+               return -ENOMEM;
+
+       dma_fence_signal(f);
+       dma_fence_put(chain);
+       return err;
+}
+
+static int unwrap_array(void *arg)
+{
+       struct dma_fence *fence, *f1, *f2, *array;
+       struct dma_fence_unwrap iter;
+       int err = 0;
+
+       f1 = mock_fence();
+       if (!f1)
+               return -ENOMEM;
+
+       f2 = mock_fence();
+       if (!f2) {
+               dma_fence_put(f1);
+               return -ENOMEM;
+       }
+
+       array = mock_array(2, f1, f2);
+       if (!array)
+               return -ENOMEM;
+
+       dma_fence_unwrap_for_each(fence, &iter, array) {
+               if (fence == f1) {
+                       f1 = NULL;
+               } else if (fence == f2) {
+                       f2 = NULL;
+               } else {
+                       pr_err("Unexpected fence!\n");
+                       err = -EINVAL;
+               }
+       }
+
+       if (f1 || f2) {
+               pr_err("Not all fences seen!\n");
+               err = -EINVAL;
+       }
+
+       dma_fence_signal(f1);
+       dma_fence_signal(f2);
+       dma_fence_put(array);
+       return 0;
+}
+
+static int unwrap_chain(void *arg)
+{
+       struct dma_fence *fence, *f1, *f2, *chain;
+       struct dma_fence_unwrap iter;
+       int err = 0;
+
+       f1 = mock_fence();
+       if (!f1)
+               return -ENOMEM;
+
+       f2 = mock_fence();
+       if (!f2) {
+               dma_fence_put(f1);
+               return -ENOMEM;
+       }
+
+       chain = mock_chain(f1, f2);
+       if (!chain)
+               return -ENOMEM;
+
+       dma_fence_unwrap_for_each(fence, &iter, chain) {
+               if (fence == f1) {
+                       f1 = NULL;
+               } else if (fence == f2) {
+                       f2 = NULL;
+               } else {
+                       pr_err("Unexpected fence!\n");
+                       err = -EINVAL;
+               }
+       }
+
+       if (f1 || f2) {
+               pr_err("Not all fences seen!\n");
+               err = -EINVAL;
+       }
+
+       dma_fence_signal(f1);
+       dma_fence_signal(f2);
+       dma_fence_put(chain);
+       return 0;
+}
+
+static int unwrap_chain_array(void *arg)
+{
+       struct dma_fence *fence, *f1, *f2, *array, *chain;
+       struct dma_fence_unwrap iter;
+       int err = 0;
+
+       f1 = mock_fence();
+       if (!f1)
+               return -ENOMEM;
+
+       f2 = mock_fence();
+       if (!f2) {
+               dma_fence_put(f1);
+               return -ENOMEM;
+       }
+
+       array = mock_array(2, f1, f2);
+       if (!array)
+               return -ENOMEM;
+
+       chain = mock_chain(NULL, array);
+       if (!chain)
+               return -ENOMEM;
+
+       dma_fence_unwrap_for_each(fence, &iter, chain) {
+               if (fence == f1) {
+                       f1 = NULL;
+               } else if (fence == f2) {
+                       f2 = NULL;
+               } else {
+                       pr_err("Unexpected fence!\n");
+                       err = -EINVAL;
+               }
+       }
+
+       if (f1 || f2) {
+               pr_err("Not all fences seen!\n");
+               err = -EINVAL;
+       }
+
+       dma_fence_signal(f1);
+       dma_fence_signal(f2);
+       dma_fence_put(chain);
+       return 0;
+}
+
+int dma_fence_unwrap(void)
+{
+       static const struct subtest tests[] = {
+               SUBTEST(sanitycheck),
+               SUBTEST(unwrap_array),
+               SUBTEST(unwrap_chain),
+               SUBTEST(unwrap_chain_array),
+       };
+
+       return subtests(tests, NULL);
+}
index 394e6e1..514d213 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (C) 2012 Google, Inc.
  */
 
+#include <linux/dma-fence-unwrap.h>
 #include <linux/export.h>
 #include <linux/file.h>
 #include <linux/fs.h>
@@ -172,20 +173,6 @@ static int sync_file_set_fence(struct sync_file *sync_file,
        return 0;
 }
 
-static struct dma_fence **get_fences(struct sync_file *sync_file,
-                                    int *num_fences)
-{
-       if (dma_fence_is_array(sync_file->fence)) {
-               struct dma_fence_array *array = to_dma_fence_array(sync_file->fence);
-
-               *num_fences = array->num_fences;
-               return array->fences;
-       }
-
-       *num_fences = 1;
-       return &sync_file->fence;
-}
-
 static void add_fence(struct dma_fence **fences,
                      int *i, struct dma_fence *fence)
 {
@@ -210,86 +197,97 @@ static void add_fence(struct dma_fence **fences,
 static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
                                         struct sync_file *b)
 {
+       struct dma_fence *a_fence, *b_fence, **fences;
+       struct dma_fence_unwrap a_iter, b_iter;
+       unsigned int index, num_fences;
        struct sync_file *sync_file;
-       struct dma_fence **fences = NULL, **nfences, **a_fences, **b_fences;
-       int i = 0, i_a, i_b, num_fences, a_num_fences, b_num_fences;
 
        sync_file = sync_file_alloc();
        if (!sync_file)
                return NULL;
 
-       a_fences = get_fences(a, &a_num_fences);
-       b_fences = get_fences(b, &b_num_fences);
-       if (a_num_fences > INT_MAX - b_num_fences)
-               goto err;
+       num_fences = 0;
+       dma_fence_unwrap_for_each(a_fence, &a_iter, a->fence)
+               ++num_fences;
+       dma_fence_unwrap_for_each(b_fence, &b_iter, b->fence)
+               ++num_fences;
 
-       num_fences = a_num_fences + b_num_fences;
+       if (num_fences > INT_MAX)
+               goto err_free_sync_file;
 
        fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
        if (!fences)
-               goto err;
+               goto err_free_sync_file;
 
        /*
-        * Assume sync_file a and b are both ordered and have no
-        * duplicates with the same context.
+        * We can't guarantee that fences in both a and b are ordered, but it is
+        * still quite likely.
         *
-        * If a sync_file can only be created with sync_file_merge
-        * and sync_file_create, this is a reasonable assumption.
+        * So attempt to order the fences as we pass over them and merge fences
+        * with the same context.
         */
-       for (i_a = i_b = 0; i_a < a_num_fences && i_b < b_num_fences; ) {
-               struct dma_fence *pt_a = a_fences[i_a];
-               struct dma_fence *pt_b = b_fences[i_b];
 
-               if (pt_a->context < pt_b->context) {
-                       add_fence(fences, &i, pt_a);
+       index = 0;
+       for (a_fence = dma_fence_unwrap_first(a->fence, &a_iter),
+            b_fence = dma_fence_unwrap_first(b->fence, &b_iter);
+            a_fence || b_fence; ) {
+
+               if (!b_fence) {
+                       add_fence(fences, &index, a_fence);
+                       a_fence = dma_fence_unwrap_next(&a_iter);
+
+               } else if (!a_fence) {
+                       add_fence(fences, &index, b_fence);
+                       b_fence = dma_fence_unwrap_next(&b_iter);
+
+               } else if (a_fence->context < b_fence->context) {
+                       add_fence(fences, &index, a_fence);
+                       a_fence = dma_fence_unwrap_next(&a_iter);
 
-                       i_a++;
-               } else if (pt_a->context > pt_b->context) {
-                       add_fence(fences, &i, pt_b);
+               } else if (b_fence->context < a_fence->context) {
+                       add_fence(fences, &index, b_fence);
+                       b_fence = dma_fence_unwrap_next(&b_iter);
+
+               } else if (__dma_fence_is_later(a_fence->seqno, b_fence->seqno,
+                                               a_fence->ops)) {
+                       add_fence(fences, &index, a_fence);
+                       a_fence = dma_fence_unwrap_next(&a_iter);
+                       b_fence = dma_fence_unwrap_next(&b_iter);
 
-                       i_b++;
                } else {
-                       if (__dma_fence_is_later(pt_a->seqno, pt_b->seqno,
-                                                pt_a->ops))
-                               add_fence(fences, &i, pt_a);
-                       else
-                               add_fence(fences, &i, pt_b);
-
-                       i_a++;
-                       i_b++;
+                       add_fence(fences, &index, b_fence);
+                       a_fence = dma_fence_unwrap_next(&a_iter);
+                       b_fence = dma_fence_unwrap_next(&b_iter);
                }
        }
 
-       for (; i_a < a_num_fences; i_a++)
-               add_fence(fences, &i, a_fences[i_a]);
-
-       for (; i_b < b_num_fences; i_b++)
-               add_fence(fences, &i, b_fences[i_b]);
-
-       if (i == 0)
-               fences[i++] = dma_fence_get(a_fences[0]);
+       if (index == 0)
+               fences[index++] = dma_fence_get_stub();
 
-       if (num_fences > i) {
-               nfences = krealloc_array(fences, i, sizeof(*fences), GFP_KERNEL);
-               if (!nfences)
-                       goto err;
+       if (num_fences > index) {
+               struct dma_fence **tmp;
 
-               fences = nfences;
+               /* Keep going even when reducing the size failed */
+               tmp = krealloc_array(fences, index, sizeof(*fences),
+                                    GFP_KERNEL);
+               if (tmp)
+                       fences = tmp;
        }
 
-       if (sync_file_set_fence(sync_file, fences, i) < 0)
-               goto err;
+       if (sync_file_set_fence(sync_file, fences, index) < 0)
+               goto err_put_fences;
 
        strlcpy(sync_file->user_name, name, sizeof(sync_file->user_name));
        return sync_file;
 
-err:
-       while (i)
-               dma_fence_put(fences[--i]);
+err_put_fences:
+       while (index)
+               dma_fence_put(fences[--index]);
        kfree(fences);
+
+err_free_sync_file:
        fput(sync_file->file);
        return NULL;
-
 }
 
 static int sync_file_release(struct inode *inode, struct file *file)
@@ -398,11 +396,13 @@ static int sync_fill_fence_info(struct dma_fence *fence,
 static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
                                       unsigned long arg)
 {
-       struct sync_file_info info;
        struct sync_fence_info *fence_info = NULL;
-       struct dma_fence **fences;
+       struct dma_fence_unwrap iter;
+       struct sync_file_info info;
+       unsigned int num_fences;
+       struct dma_fence *fence;
+       int ret;
        __u32 size;
-       int num_fences, ret, i;
 
        if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
                return -EFAULT;
@@ -410,7 +410,9 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
        if (info.flags || info.pad)
                return -EINVAL;
 
-       fences = get_fences(sync_file, &num_fences);
+       num_fences = 0;
+       dma_fence_unwrap_for_each(fence, &iter, sync_file->fence)
+               ++num_fences;
 
        /*
         * Passing num_fences = 0 means that userspace doesn't want to
@@ -433,8 +435,11 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
        if (!fence_info)
                return -ENOMEM;
 
-       for (i = 0; i < num_fences; i++) {
-               int status = sync_fill_fence_info(fences[i], &fence_info[i]);
+       num_fences = 0;
+       dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) {
+               int status;
+
+               status = sync_fill_fence_info(fence, &fence_info[num_fences++]);
                info.status = info.status <= 0 ? info.status : status;
        }
 
index 026e4e2..f4df344 100644 (file)
@@ -214,6 +214,29 @@ int drm_of_encoder_active_endpoint(struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
 
+static int find_panel_or_bridge(struct device_node *node,
+                               struct drm_panel **panel,
+                               struct drm_bridge **bridge)
+{
+       if (panel) {
+               *panel = of_drm_find_panel(node);
+               if (!IS_ERR(*panel))
+                       return 0;
+
+               /* Clear the panel pointer in case of error. */
+               *panel = NULL;
+       }
+
+       /* No panel found yet, check for a bridge next. */
+       if (bridge) {
+               *bridge = of_drm_find_bridge(node);
+               if (*bridge)
+                       return 0;
+       }
+
+       return -EPROBE_DEFER;
+}
+
 /**
  * drm_of_find_panel_or_bridge - return connected panel or bridge device
  * @np: device tree node containing encoder output ports
@@ -236,66 +259,44 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
                                struct drm_panel **panel,
                                struct drm_bridge **bridge)
 {
-       int ret = -EPROBE_DEFER;
-       struct device_node *remote;
+       struct device_node *node;
+       int ret;
 
        if (!panel && !bridge)
                return -EINVAL;
+
        if (panel)
                *panel = NULL;
-
-       /**
-        * Devices can also be child nodes when we also control that device
-        * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
-        *
-        * Lookup for a child node of the given parent that isn't either port
-        * or ports.
-        */
-       for_each_available_child_of_node(np, remote) {
-               if (of_node_name_eq(remote, "port") ||
-                   of_node_name_eq(remote, "ports"))
-                       continue;
-
-               goto of_find_panel_or_bridge;
+       if (bridge)
+               *bridge = NULL;
+
+       /* Check for a graph on the device node first. */
+       if (of_graph_is_present(np)) {
+               node = of_graph_get_remote_node(np, port, endpoint);
+               if (node) {
+                       ret = find_panel_or_bridge(node, panel, bridge);
+                       of_node_put(node);
+
+                       if (!ret)
+                               return 0;
+               }
        }
 
-       /*
-        * of_graph_get_remote_node() produces a noisy error message if port
-        * node isn't found and the absence of the port is a legit case here,
-        * so at first we silently check whether graph presents in the
-        * device-tree node.
-        */
-       if (!of_graph_is_present(np))
-               return -ENODEV;
-
-       remote = of_graph_get_remote_node(np, port, endpoint);
-
-of_find_panel_or_bridge:
-       if (!remote)
-               return -ENODEV;
+       /* Otherwise check for any child node other than port/ports. */
+       for_each_available_child_of_node(np, node) {
+               if (of_node_name_eq(node, "port") ||
+                   of_node_name_eq(node, "ports"))
+                       continue;
 
-       if (panel) {
-               *panel = of_drm_find_panel(remote);
-               if (!IS_ERR(*panel))
-                       ret = 0;
-               else
-                       *panel = NULL;
-       }
-
-       /* No panel found yet, check for a bridge next. */
-       if (bridge) {
-               if (ret) {
-                       *bridge = of_drm_find_bridge(remote);
-                       if (*bridge)
-                               ret = 0;
-               } else {
-                       *bridge = NULL;
-               }
+               ret = find_panel_or_bridge(node, panel, bridge);
+               of_node_put(node);
 
+               /* Stop at the first found occurrence. */
+               if (!ret)
+                       return 0;
        }
 
-       of_node_put(remote);
-       return ret;
+       return -EPROBE_DEFER;
 }
 EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
 
index fec374f..ec7f25d 100644 (file)
@@ -61,6 +61,21 @@ to_dma_fence_array(struct dma_fence *fence)
        return container_of(fence, struct dma_fence_array, base);
 }
 
+/**
+ * dma_fence_array_for_each - iterate over all fences in array
+ * @fence: current fence
+ * @index: index into the array
+ * @head: potential dma_fence_array object
+ *
+ * Test if @array is a dma_fence_array object and if yes iterate over all fences
+ * in the array. If not just iterate over the fence in @array itself.
+ *
+ * For a deep dive iterator see dma_fence_unwrap_for_each().
+ */
+#define dma_fence_array_for_each(fence, index, head)                   \
+       for (index = 0, fence = dma_fence_array_first(head); fence;     \
+            ++(index), fence = dma_fence_array_next(head, index))
+
 struct dma_fence_array *dma_fence_array_create(int num_fences,
                                               struct dma_fence **fences,
                                               u64 context, unsigned seqno,
@@ -68,4 +83,8 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
 
 bool dma_fence_match_context(struct dma_fence *fence, u64 context);
 
+struct dma_fence *dma_fence_array_first(struct dma_fence *head);
+struct dma_fence *dma_fence_array_next(struct dma_fence *head,
+                                      unsigned int index);
+
 #endif /* __LINUX_DMA_FENCE_ARRAY_H */
index 10d51bc..4bdf0b9 100644 (file)
@@ -112,6 +112,8 @@ static inline void dma_fence_chain_free(struct dma_fence_chain *chain)
  *
  * Iterate over all fences in the chain. We keep a reference to the current
  * fence while inside the loop which must be dropped when breaking out.
+ *
+ * For a deep dive iterator see dma_fence_unwrap_for_each().
  */
 #define dma_fence_chain_for_each(iter, head)   \
        for (iter = dma_fence_get(head); iter; \
diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h
new file mode 100644 (file)
index 0000000..77e335a
--- /dev/null
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * fence-chain: chain fences together in a timeline
+ *
+ * Copyright (C) 2022 Advanced Micro Devices, Inc.
+ * Authors:
+ *     Christian König <christian.koenig@amd.com>
+ */
+
+#ifndef __LINUX_DMA_FENCE_UNWRAP_H
+#define __LINUX_DMA_FENCE_UNWRAP_H
+
+#include <linux/dma-fence-chain.h>
+#include <linux/dma-fence-array.h>
+
+/**
+ * struct dma_fence_unwrap - cursor into the container structure
+ *
+ * Should be used with dma_fence_unwrap_for_each() iterator macro.
+ */
+struct dma_fence_unwrap {
+       /**
+        * @chain: potential dma_fence_chain, but can be other fence as well
+        */
+       struct dma_fence *chain;
+       /**
+        * @array: potential dma_fence_array, but can be other fence as well
+        */
+       struct dma_fence *array;
+       /**
+        * @index: last returned index if @array is really a dma_fence_array
+        */
+       unsigned int index;
+};
+
+/* Internal helper to start new array iteration, don't use directly */
+static inline struct dma_fence *
+__dma_fence_unwrap_array(struct dma_fence_unwrap * cursor)
+{
+       cursor->array = dma_fence_chain_contained(cursor->chain);
+       cursor->index = 0;
+       return dma_fence_array_first(cursor->array);
+}
+
+/**
+ * dma_fence_unwrap_first - return the first fence from fence containers
+ * @head: the entrypoint into the containers
+ * @cursor: current position inside the containers
+ *
+ * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
+ * first fence.
+ */
+static inline struct dma_fence *
+dma_fence_unwrap_first(struct dma_fence *head, struct dma_fence_unwrap *cursor)
+{
+       cursor->chain = dma_fence_get(head);
+       return __dma_fence_unwrap_array(cursor);
+}
+
+/**
+ * dma_fence_unwrap_next - return the next fence from a fence containers
+ * @cursor: current position inside the containers
+ *
+ * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
+ * the next fence from them.
+ */
+static inline struct dma_fence *
+dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
+{
+       struct dma_fence *tmp;
+
+       ++cursor->index;
+       tmp = dma_fence_array_next(cursor->array, cursor->index);
+       if (tmp)
+               return tmp;
+
+       cursor->chain = dma_fence_chain_walk(cursor->chain);
+       return __dma_fence_unwrap_array(cursor);
+}
+
+/**
+ * dma_fence_unwrap_for_each - iterate over all fences in containers
+ * @fence: current fence
+ * @cursor: current position inside the containers
+ * @head: starting point for the iterator
+ *
+ * Unwrap dma_fence_chain and dma_fence_array containers and deep dive into all
+ * potential fences in them. If @head is just a normal fence only that one is
+ * returned.
+ */
+#define dma_fence_unwrap_for_each(fence, cursor, head)                 \
+       for (fence = dma_fence_unwrap_first(head, cursor); fence;       \
+            fence = dma_fence_unwrap_next(cursor))
+
+#endif