drm-misc-next-fixes for v5.18-rc2:

- fix warning about fence containers
 - fix logic error in new fence merge code
 - handle empty dma_fence_arrays gracefully
 - Try all possible cases for bridge/panel detection.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmJOwqsACgkQ/lWMcqZw
 E8NdAw//YQB9FuU3x0JmUCa4SY63mnNyQXUz2ZRe6DV39nSOqrGV0QlZr538/oJk
 HfSyAOs+zLYsZSvZNmQ4zgSlyU/IcAR68lB1wVgcVHn3XKPq0Dxxd0kOg9aQ5Hpd
 xF2wIxeMmHj5BJWe+S6IIWdCyWMRneT9R5lFLI59S08A3yGAY2x4u+8G+YpH1bZa
 cfxOfpHWCbgwCbjpjcVFd1JQAo9EcqHlebvNqpZzf5RPYIVaYRQrx+p0e1u6rjxc
 EVRube1dxhx13O1k4EGA4ghornl8uDpvaYU09L83xUHgYDehO7XmlwN2YCZZE09Y
 ZA2OVJ9Hh3RSYa8bQQ5AXlKddcYkmWJa7Zvvi1u6CVBW7pNNPYm8ryMXppIqMW2q
 2Lmc5LsZnNCHshgjQz9L5P4IBZh1EQ97ti4efRLvx4DSCMyBmBSHVicfs8m0HhlO
 jNPmmJJjMxWzTtx1UvI3fmSFxKsSYBC4eTa/ihspFmuGT0U5+WdPDFEGn1NrykCX
 nAHn0ldfJUfBgYis7kk1qNH4hqNH7EsLu7SH68jqCAdxMhz4pzk54VDSFX/qtmUx
 tP+4Z71/jMV5di3l27rftCgUBfnX3Q9KJoRIx02J87QgsPjiCwOBIWTC4viKk8cq
 JBkRH+U8LhmqUDwaKvOsNsO/ReNm+seWU6s2aELnLOB2KLDiIJk=
 =MY3d
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-fixes-2022-04-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-fixes

drm-misc-next-fixes for v5.18-rc2:
- fix warning about fence containers
- fix logic error in new fence merge code
- handle empty dma_fence_arrays gracefully
- Try all possible cases for bridge/panel detection.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/3b8e6439-612e-f640-e380-51e834393e94@linux.intel.com
This commit is contained in:
Dave Airlie 2022-04-08 09:13:33 +10:00
commit 995f748073
10 changed files with 541 additions and 118 deletions

View 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
~~~~~~~~~~~~~~~~~~~~~~~~

View 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

View 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);

View 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)

View File

@ -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);
}

View 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; ) {
i_a++;
} else if (pt_a->context > pt_b->context) {
add_fence(fences, &i, pt_b);
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);
} 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]);
if (index == 0)
fences[index++] = dma_fence_get_stub();
for (; i_b < b_num_fences; i_b++)
add_fence(fences, &i, b_fences[i_b]);
if (num_fences > index) {
struct dma_fence **tmp;
if (i == 0)
fences[i++] = dma_fence_get(a_fences[0]);
if (num_fences > i) {
nfences = krealloc_array(fences, i, sizeof(*fences), GFP_KERNEL);
if (!nfences)
goto err;
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;
}

View 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;
if (bridge)
*bridge = 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"))
/* 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;
}
}
/* 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;
goto of_find_panel_or_bridge;
ret = find_panel_or_bridge(node, panel, bridge);
of_node_put(node);
/* Stop at the first found occurrence. */
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;
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;
}
}
of_node_put(remote);
return ret;
return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);

View 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 */

View 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; \

View File

@ -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