diff mbox series

[2/4] dma-buf: add dma_resv_get_singleton_rcu (v4)

Message ID 20210520190007.534046-3-jason@jlekstrand.net (mailing list archive)
State New, archived
Headers show
Series dma-buf: Add an API for exporting sync files (v8) | expand

Commit Message

Jason Ekstrand May 20, 2021, 7 p.m. UTC
Add a helper function to get a single fence representing
all fences in a dma_resv object.

This fence is either the only one in the object or all not
signaled fences of the object in a flatted out dma_fence_array.

v2 (Jason Ekstrand):
 - Take reference of fences both for creating the dma_fence_array and in
   the case where we return one fence.
 - Handle the case where dma_resv_get_list() returns NULL

v3 (Jason Ekstrand):
 - Add an _rcu suffix because it is read-only
 - Rewrite to use dma_resv_get_fences_rcu so it's RCU-safe
 - Add an EXPORT_SYMBOL_GPL declaration
 - Re-author the patch to Jason since very little is left of Christian
   König's original patch
 - Remove the extra fence argument

v4 (Jason Ekstrand):
 - Restore the extra fence argument

Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>

get_singleton
---
 drivers/dma-buf/dma-resv.c | 122 +++++++++++++++++++++++++++++++++++++
 include/linux/dma-resv.h   |   3 +
 2 files changed, 125 insertions(+)

Comments

Daniel Vetter May 21, 2021, 5:48 p.m. UTC | #1
On Thu, May 20, 2021 at 02:00:05PM -0500, Jason Ekstrand wrote:
> Add a helper function to get a single fence representing
> all fences in a dma_resv object.
> 
> This fence is either the only one in the object or all not
> signaled fences of the object in a flatted out dma_fence_array.
> 
> v2 (Jason Ekstrand):
>  - Take reference of fences both for creating the dma_fence_array and in
>    the case where we return one fence.
>  - Handle the case where dma_resv_get_list() returns NULL
> 
> v3 (Jason Ekstrand):
>  - Add an _rcu suffix because it is read-only
>  - Rewrite to use dma_resv_get_fences_rcu so it's RCU-safe
>  - Add an EXPORT_SYMBOL_GPL declaration
>  - Re-author the patch to Jason since very little is left of Christian
>    König's original patch
>  - Remove the extra fence argument
> 
> v4 (Jason Ekstrand):
>  - Restore the extra fence argument
> 
> Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
> 
> get_singleton

Spurious thing here.

> ---
>  drivers/dma-buf/dma-resv.c | 122 +++++++++++++++++++++++++++++++++++++
>  include/linux/dma-resv.h   |   3 +
>  2 files changed, 125 insertions(+)
> 
> diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
> index 6ddbeb5dfbf65..25995fc15c370 100644
> --- a/drivers/dma-buf/dma-resv.c
> +++ b/drivers/dma-buf/dma-resv.c
> @@ -33,6 +33,8 @@
>   */
>  
>  #include <linux/dma-resv.h>
> +#include <linux/dma-fence-chain.h>
> +#include <linux/dma-fence-array.h>
>  #include <linux/export.h>
>  #include <linux/mm.h>
>  #include <linux/sched/mm.h>
> @@ -49,6 +51,19 @@
>   * write-side updates.
>   */
>  
> +/**
> + * dma_fence_deep_dive_for_each - deep dive into the fence containers
> + * @fence: resulting fence
> + * @chain: variable for a dma_fence_chain
> + * @index: index into a dma_fence_array
> + * @head: starting point
> + *
> + * Helper to deep dive into the fence containers for flattening them.
> + */
> +#define dma_fence_deep_dive_for_each(fence, chain, index, head)	\
> +	dma_fence_chain_for_each(chain, head)			\
> +		dma_fence_array_for_each(fence, index, chain)

Since this is is just internal helper in the .c file we generally don't
document it. Maybe small comment if you feel it's worth it.

> +
>  DEFINE_WD_CLASS(reservation_ww_class);
>  EXPORT_SYMBOL(reservation_ww_class);
>  
> @@ -517,6 +532,113 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
>  }
>  EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
>  
> +/**
> + * dma_resv_get_singleton - get a single fence for the dma_resv object

Name doesn't match here.

> + * @obj: the reservation object
> + * @extra: extra fence to add to the resulting array
> + * @result: resulting dma_fence
> + *
> + * Get a single fence representing all unsignaled fences in the dma_resv object
> + * plus the given extra fence. If we got only one fence return a new
> + * reference to that, otherwise return a dma_fence_array object.
> + *
> + * RETURNS
> + * Returns -NOMEM if allocations fail, zero otherwise.

Kernel often encodes this in ERR_PTR so that you don't have to pass a
pointer to store the result. Would feel more kerenl-y I think that way. So
no result parameter, and on alloc failure you'd return

	return ERR_PTR(-ENOMEM);

> + */
> +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,

tbh the _rcu here is confusing. I think _unlocked is the better suffix,
maybe we should rename dma_resv_get_fences_rcu too for consistency. The
rcu-ness of the lookup isn't leaked to callers at all, so no point giving
them a panic.

> +			       struct dma_fence **result)
> +{
> +	struct dma_fence **resv_fences, *fence, *chain, **fences;
> +	struct dma_fence_array *array;
> +	unsigned int num_resv_fences, num_fences;
> +	unsigned int ret, i, j;
> +
> +	ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
> +	if (ret)
> +		return ret;
> +
> +	num_fences = 0;
> +	*result = NULL;
> +
> +	if (num_resv_fences == 0 && !extra)
> +		return 0;
> +
> +	for (i = 0; i < num_resv_fences; ++i) {
> +		dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> +			if (dma_fence_is_signaled(fence))
> +				continue;
> +
> +			*result = fence;
> +			++num_fences;
> +		}
> +	}
> +
> +	if (extra) {
> +		dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> +			if (dma_fence_is_signaled(fence))
> +				continue;
> +
> +			*result = fence;
> +			++num_fences;
> +		}
> +	}
> +
> +	if (num_fences <= 1) {
> +		*result = dma_fence_get(*result);
> +		goto put_resv_fences;
> +	}
> +
> +	fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
> +			       GFP_KERNEL);
> +	if (!fences) {
> +		*result = NULL;
> +		ret = -ENOMEM;
> +		goto put_resv_fences;
> +	}
> +
> +	num_fences = 0;
> +	for (i = 0; i < num_resv_fences; ++i) {
> +		dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> +			if (!dma_fence_is_signaled(fence))
> +				fences[num_fences++] = dma_fence_get(fence);
> +		}
> +	}
> +
> +	if (extra) {
> +		dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> +			if (dma_fence_is_signaled(fence))
> +				fences[num_fences++] = dma_fence_get(fence);
> +		}
> +	}
> +
> +	if (num_fences <= 1) {
> +		*result = num_fences ? fences[0] : NULL;
> +		kfree(fences);
> +		goto put_resv_fences;
> +	}
> +
> +	array = dma_fence_array_create(num_fences, fences,
> +				       dma_fence_context_alloc(1),
> +				       1, false);
> +	if (array) {
> +		*result = &array->base;
> +	} else {
> +		*result = NULL;
> +		while (num_fences--)
> +			dma_fence_put(fences[num_fences]);
> +		kfree(fences);
> +		ret = -ENOMEM;
> +	}
> +
> +put_resv_fences:
> +	while (num_resv_fences--)
> +		dma_fence_put(resv_fences[num_resv_fences]);
> +	kfree(resv_fences);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);

With the nits addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> +
>  /**
>   * dma_resv_wait_timeout_rcu - Wait on reservation's objects
>   * shared and/or exclusive fences.
> diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
> index d44a77e8a7e34..d49ca263e78b4 100644
> --- a/include/linux/dma-resv.h
> +++ b/include/linux/dma-resv.h
> @@ -285,6 +285,9 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
>  
>  int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
>  
> +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
> +			       struct dma_fence **result);
> +
>  long dma_resv_wait_timeout_rcu(struct dma_resv *obj, bool wait_all, bool intr,
>  			       unsigned long timeout);
>  
> -- 
> 2.31.1
>
kernel test robot May 22, 2021, 2:38 p.m. UTC | #2
Hi Jason,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on tegra-drm/drm/tegra/for-next]
[also build test WARNING on linus/master v5.13-rc2 next-20210521]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Jason-Ekstrand/dma-buf-Add-an-API-for-exporting-sync-files-v8/20210522-201251
base:   git://anongit.freedesktop.org/tegra/linux.git drm/tegra/for-next
config: x86_64-randconfig-s031-20210522 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.3-341-g8af24329-dirty
        # https://github.com/0day-ci/linux/commit/925221f402201e7b1f665619dda2c5ee6d6324f1
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Jason-Ekstrand/dma-buf-Add-an-API-for-exporting-sync-files-v8/20210522-201251
        git checkout 925221f402201e7b1f665619dda2c5ee6d6324f1
        # save the attached .config to linux build tree
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/dma-buf/dma-resv.c:550: warning: expecting prototype for dma_resv_get_singleton(). Prototype was for dma_resv_get_singleton_rcu() instead


vim +550 drivers/dma-buf/dma-resv.c

   534	
   535	/**
   536	 * dma_resv_get_singleton - get a single fence for the dma_resv object
   537	 * @obj: the reservation object
   538	 * @extra: extra fence to add to the resulting array
   539	 * @result: resulting dma_fence
   540	 *
   541	 * Get a single fence representing all unsignaled fences in the dma_resv object
   542	 * plus the given extra fence. If we got only one fence return a new
   543	 * reference to that, otherwise return a dma_fence_array object.
   544	 *
   545	 * RETURNS
   546	 * Returns -NOMEM if allocations fail, zero otherwise.
   547	 */
   548	int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
   549				       struct dma_fence **result)
 > 550	{
   551		struct dma_fence **resv_fences, *fence, *chain, **fences;
   552		struct dma_fence_array *array;
   553		unsigned int num_resv_fences, num_fences;
   554		unsigned int ret, i, j;
   555	
   556		ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
   557		if (ret)
   558			return ret;
   559	
   560		num_fences = 0;
   561		*result = NULL;
   562	
   563		if (num_resv_fences == 0 && !extra)
   564			return 0;
   565	
   566		for (i = 0; i < num_resv_fences; ++i) {
   567			dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
   568				if (dma_fence_is_signaled(fence))
   569					continue;
   570	
   571				*result = fence;
   572				++num_fences;
   573			}
   574		}
   575	
   576		if (extra) {
   577			dma_fence_deep_dive_for_each(fence, chain, j, extra) {
   578				if (dma_fence_is_signaled(fence))
   579					continue;
   580	
   581				*result = fence;
   582				++num_fences;
   583			}
   584		}
   585	
   586		if (num_fences <= 1) {
   587			*result = dma_fence_get(*result);
   588			goto put_resv_fences;
   589		}
   590	
   591		fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
   592				       GFP_KERNEL);
   593		if (!fences) {
   594			*result = NULL;
   595			ret = -ENOMEM;
   596			goto put_resv_fences;
   597		}
   598	
   599		num_fences = 0;
   600		for (i = 0; i < num_resv_fences; ++i) {
   601			dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
   602				if (!dma_fence_is_signaled(fence))
   603					fences[num_fences++] = dma_fence_get(fence);
   604			}
   605		}
   606	
   607		if (extra) {
   608			dma_fence_deep_dive_for_each(fence, chain, j, extra) {
   609				if (dma_fence_is_signaled(fence))
   610					fences[num_fences++] = dma_fence_get(fence);
   611			}
   612		}
   613	
   614		if (num_fences <= 1) {
   615			*result = num_fences ? fences[0] : NULL;
   616			kfree(fences);
   617			goto put_resv_fences;
   618		}
   619	
   620		array = dma_fence_array_create(num_fences, fences,
   621					       dma_fence_context_alloc(1),
   622					       1, false);
   623		if (array) {
   624			*result = &array->base;
   625		} else {
   626			*result = NULL;
   627			while (num_fences--)
   628				dma_fence_put(fences[num_fences]);
   629			kfree(fences);
   630			ret = -ENOMEM;
   631		}
   632	
   633	put_resv_fences:
   634		while (num_resv_fences--)
   635			dma_fence_put(resv_fences[num_resv_fences]);
   636		kfree(resv_fences);
   637	
   638		return ret;
   639	}
   640	EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
   641	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot May 22, 2021, 6:59 p.m. UTC | #3
Hi Jason,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on tegra-drm/drm/tegra/for-next]
[also build test WARNING on linus/master v5.13-rc2 next-20210521]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Jason-Ekstrand/dma-buf-Add-an-API-for-exporting-sync-files-v8/20210522-201251
base:   git://anongit.freedesktop.org/tegra/linux.git drm/tegra/for-next
config: x86_64-randconfig-a013-20210522 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project e84a9b9bb3051c35dea993cdad7b3d2575638f85)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/925221f402201e7b1f665619dda2c5ee6d6324f1
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Jason-Ekstrand/dma-buf-Add-an-API-for-exporting-sync-files-v8/20210522-201251
        git checkout 925221f402201e7b1f665619dda2c5ee6d6324f1
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/dma-buf/dma-resv.c:550: warning: expecting prototype for dma_resv_get_singleton(). Prototype was for dma_resv_get_singleton_rcu() instead


vim +550 drivers/dma-buf/dma-resv.c

   534	
   535	/**
   536	 * dma_resv_get_singleton - get a single fence for the dma_resv object
   537	 * @obj: the reservation object
   538	 * @extra: extra fence to add to the resulting array
   539	 * @result: resulting dma_fence
   540	 *
   541	 * Get a single fence representing all unsignaled fences in the dma_resv object
   542	 * plus the given extra fence. If we got only one fence return a new
   543	 * reference to that, otherwise return a dma_fence_array object.
   544	 *
   545	 * RETURNS
   546	 * Returns -NOMEM if allocations fail, zero otherwise.
   547	 */
   548	int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
   549				       struct dma_fence **result)
 > 550	{
   551		struct dma_fence **resv_fences, *fence, *chain, **fences;
   552		struct dma_fence_array *array;
   553		unsigned int num_resv_fences, num_fences;
   554		unsigned int ret, i, j;
   555	
   556		ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
   557		if (ret)
   558			return ret;
   559	
   560		num_fences = 0;
   561		*result = NULL;
   562	
   563		if (num_resv_fences == 0 && !extra)
   564			return 0;
   565	
   566		for (i = 0; i < num_resv_fences; ++i) {
   567			dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
   568				if (dma_fence_is_signaled(fence))
   569					continue;
   570	
   571				*result = fence;
   572				++num_fences;
   573			}
   574		}
   575	
   576		if (extra) {
   577			dma_fence_deep_dive_for_each(fence, chain, j, extra) {
   578				if (dma_fence_is_signaled(fence))
   579					continue;
   580	
   581				*result = fence;
   582				++num_fences;
   583			}
   584		}
   585	
   586		if (num_fences <= 1) {
   587			*result = dma_fence_get(*result);
   588			goto put_resv_fences;
   589		}
   590	
   591		fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
   592				       GFP_KERNEL);
   593		if (!fences) {
   594			*result = NULL;
   595			ret = -ENOMEM;
   596			goto put_resv_fences;
   597		}
   598	
   599		num_fences = 0;
   600		for (i = 0; i < num_resv_fences; ++i) {
   601			dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
   602				if (!dma_fence_is_signaled(fence))
   603					fences[num_fences++] = dma_fence_get(fence);
   604			}
   605		}
   606	
   607		if (extra) {
   608			dma_fence_deep_dive_for_each(fence, chain, j, extra) {
   609				if (dma_fence_is_signaled(fence))
   610					fences[num_fences++] = dma_fence_get(fence);
   611			}
   612		}
   613	
   614		if (num_fences <= 1) {
   615			*result = num_fences ? fences[0] : NULL;
   616			kfree(fences);
   617			goto put_resv_fences;
   618		}
   619	
   620		array = dma_fence_array_create(num_fences, fences,
   621					       dma_fence_context_alloc(1),
   622					       1, false);
   623		if (array) {
   624			*result = &array->base;
   625		} else {
   626			*result = NULL;
   627			while (num_fences--)
   628				dma_fence_put(fences[num_fences]);
   629			kfree(fences);
   630			ret = -ENOMEM;
   631		}
   632	
   633	put_resv_fences:
   634		while (num_resv_fences--)
   635			dma_fence_put(resv_fences[num_resv_fences]);
   636		kfree(resv_fences);
   637	
   638		return ret;
   639	}
   640	EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
   641	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Jason Ekstrand May 24, 2021, 8:04 p.m. UTC | #4
On Fri, May 21, 2021 at 12:48 PM Daniel Vetter <daniel@ffwll.ch> wrote:
>
> On Thu, May 20, 2021 at 02:00:05PM -0500, Jason Ekstrand wrote:
> > Add a helper function to get a single fence representing
> > all fences in a dma_resv object.
> >
> > This fence is either the only one in the object or all not
> > signaled fences of the object in a flatted out dma_fence_array.
> >
> > v2 (Jason Ekstrand):
> >  - Take reference of fences both for creating the dma_fence_array and in
> >    the case where we return one fence.
> >  - Handle the case where dma_resv_get_list() returns NULL
> >
> > v3 (Jason Ekstrand):
> >  - Add an _rcu suffix because it is read-only
> >  - Rewrite to use dma_resv_get_fences_rcu so it's RCU-safe
> >  - Add an EXPORT_SYMBOL_GPL declaration
> >  - Re-author the patch to Jason since very little is left of Christian
> >    König's original patch
> >  - Remove the extra fence argument
> >
> > v4 (Jason Ekstrand):
> >  - Restore the extra fence argument
> >
> > Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
> >
> > get_singleton
>
> Spurious thing here.

Fixed.

> > ---
> >  drivers/dma-buf/dma-resv.c | 122 +++++++++++++++++++++++++++++++++++++
> >  include/linux/dma-resv.h   |   3 +
> >  2 files changed, 125 insertions(+)
> >
> > diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
> > index 6ddbeb5dfbf65..25995fc15c370 100644
> > --- a/drivers/dma-buf/dma-resv.c
> > +++ b/drivers/dma-buf/dma-resv.c
> > @@ -33,6 +33,8 @@
> >   */
> >
> >  #include <linux/dma-resv.h>
> > +#include <linux/dma-fence-chain.h>
> > +#include <linux/dma-fence-array.h>
> >  #include <linux/export.h>
> >  #include <linux/mm.h>
> >  #include <linux/sched/mm.h>
> > @@ -49,6 +51,19 @@
> >   * write-side updates.
> >   */
> >
> > +/**
> > + * dma_fence_deep_dive_for_each - deep dive into the fence containers
> > + * @fence: resulting fence
> > + * @chain: variable for a dma_fence_chain
> > + * @index: index into a dma_fence_array
> > + * @head: starting point
> > + *
> > + * Helper to deep dive into the fence containers for flattening them.
> > + */
> > +#define dma_fence_deep_dive_for_each(fence, chain, index, head)      \
> > +     dma_fence_chain_for_each(chain, head)                   \
> > +             dma_fence_array_for_each(fence, index, chain)
>
> Since this is is just internal helper in the .c file we generally don't
> document it. Maybe small comment if you feel it's worth it.

Sure, I can write LESS documentation. :-P

> > +
> >  DEFINE_WD_CLASS(reservation_ww_class);
> >  EXPORT_SYMBOL(reservation_ww_class);
> >
> > @@ -517,6 +532,113 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
> >  }
> >  EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
> >
> > +/**
> > + * dma_resv_get_singleton - get a single fence for the dma_resv object
>
> Name doesn't match here.

Fixed.

> > + * @obj: the reservation object
> > + * @extra: extra fence to add to the resulting array
> > + * @result: resulting dma_fence
> > + *
> > + * Get a single fence representing all unsignaled fences in the dma_resv object
> > + * plus the given extra fence. If we got only one fence return a new
> > + * reference to that, otherwise return a dma_fence_array object.
> > + *
> > + * RETURNS
> > + * Returns -NOMEM if allocations fail, zero otherwise.
>
> Kernel often encodes this in ERR_PTR so that you don't have to pass a
> pointer to store the result. Would feel more kerenl-y I think that way. So
> no result parameter, and on alloc failure you'd return

Done.

>         return ERR_PTR(-ENOMEM);
>
> > + */
> > +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
>
> tbh the _rcu here is confusing. I think _unlocked is the better suffix,
> maybe we should rename dma_resv_get_fences_rcu too for consistency. The
> rcu-ness of the lookup isn't leaked to callers at all, so no point giving
> them a panic.

I can make that change.  I'll also include a patch in the next re-send
that renames all the _rcu helpers to _unlocked for consistency.

--Jason

> > +                            struct dma_fence **result)
> > +{
> > +     struct dma_fence **resv_fences, *fence, *chain, **fences;
> > +     struct dma_fence_array *array;
> > +     unsigned int num_resv_fences, num_fences;
> > +     unsigned int ret, i, j;
> > +
> > +     ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
> > +     if (ret)
> > +             return ret;
> > +
> > +     num_fences = 0;
> > +     *result = NULL;
> > +
> > +     if (num_resv_fences == 0 && !extra)
> > +             return 0;
> > +
> > +     for (i = 0; i < num_resv_fences; ++i) {
> > +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> > +                     if (dma_fence_is_signaled(fence))
> > +                             continue;
> > +
> > +                     *result = fence;
> > +                     ++num_fences;
> > +             }
> > +     }
> > +
> > +     if (extra) {
> > +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> > +                     if (dma_fence_is_signaled(fence))
> > +                             continue;
> > +
> > +                     *result = fence;
> > +                     ++num_fences;
> > +             }
> > +     }
> > +
> > +     if (num_fences <= 1) {
> > +             *result = dma_fence_get(*result);
> > +             goto put_resv_fences;
> > +     }
> > +
> > +     fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
> > +                            GFP_KERNEL);
> > +     if (!fences) {
> > +             *result = NULL;
> > +             ret = -ENOMEM;
> > +             goto put_resv_fences;
> > +     }
> > +
> > +     num_fences = 0;
> > +     for (i = 0; i < num_resv_fences; ++i) {
> > +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> > +                     if (!dma_fence_is_signaled(fence))
> > +                             fences[num_fences++] = dma_fence_get(fence);
> > +             }
> > +     }
> > +
> > +     if (extra) {
> > +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> > +                     if (dma_fence_is_signaled(fence))
> > +                             fences[num_fences++] = dma_fence_get(fence);
> > +             }
> > +     }
> > +
> > +     if (num_fences <= 1) {
> > +             *result = num_fences ? fences[0] : NULL;
> > +             kfree(fences);
> > +             goto put_resv_fences;
> > +     }
> > +
> > +     array = dma_fence_array_create(num_fences, fences,
> > +                                    dma_fence_context_alloc(1),
> > +                                    1, false);
> > +     if (array) {
> > +             *result = &array->base;
> > +     } else {
> > +             *result = NULL;
> > +             while (num_fences--)
> > +                     dma_fence_put(fences[num_fences]);
> > +             kfree(fences);
> > +             ret = -ENOMEM;
> > +     }
> > +
> > +put_resv_fences:
> > +     while (num_resv_fences--)
> > +             dma_fence_put(resv_fences[num_resv_fences]);
> > +     kfree(resv_fences);
> > +
> > +     return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
>
> With the nits addressed:
>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
> > +
> >  /**
> >   * dma_resv_wait_timeout_rcu - Wait on reservation's objects
> >   * shared and/or exclusive fences.
> > diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
> > index d44a77e8a7e34..d49ca263e78b4 100644
> > --- a/include/linux/dma-resv.h
> > +++ b/include/linux/dma-resv.h
> > @@ -285,6 +285,9 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
> >
> >  int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
> >
> > +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
> > +                            struct dma_fence **result);
> > +
> >  long dma_resv_wait_timeout_rcu(struct dma_resv *obj, bool wait_all, bool intr,
> >                              unsigned long timeout);
> >
> > --
> > 2.31.1
> >
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
Daniel Vetter May 25, 2021, 12:38 p.m. UTC | #5
On Mon, May 24, 2021 at 03:04:35PM -0500, Jason Ekstrand wrote:
> On Fri, May 21, 2021 at 12:48 PM Daniel Vetter <daniel@ffwll.ch> wrote:
> >
> > On Thu, May 20, 2021 at 02:00:05PM -0500, Jason Ekstrand wrote:
> > > Add a helper function to get a single fence representing
> > > all fences in a dma_resv object.
> > >
> > > This fence is either the only one in the object or all not
> > > signaled fences of the object in a flatted out dma_fence_array.
> > >
> > > v2 (Jason Ekstrand):
> > >  - Take reference of fences both for creating the dma_fence_array and in
> > >    the case where we return one fence.
> > >  - Handle the case where dma_resv_get_list() returns NULL
> > >
> > > v3 (Jason Ekstrand):
> > >  - Add an _rcu suffix because it is read-only
> > >  - Rewrite to use dma_resv_get_fences_rcu so it's RCU-safe
> > >  - Add an EXPORT_SYMBOL_GPL declaration
> > >  - Re-author the patch to Jason since very little is left of Christian
> > >    König's original patch
> > >  - Remove the extra fence argument
> > >
> > > v4 (Jason Ekstrand):
> > >  - Restore the extra fence argument
> > >
> > > Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
> > >
> > > get_singleton
> >
> > Spurious thing here.
> 
> Fixed.
> 
> > > ---
> > >  drivers/dma-buf/dma-resv.c | 122 +++++++++++++++++++++++++++++++++++++
> > >  include/linux/dma-resv.h   |   3 +
> > >  2 files changed, 125 insertions(+)
> > >
> > > diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
> > > index 6ddbeb5dfbf65..25995fc15c370 100644
> > > --- a/drivers/dma-buf/dma-resv.c
> > > +++ b/drivers/dma-buf/dma-resv.c
> > > @@ -33,6 +33,8 @@
> > >   */
> > >
> > >  #include <linux/dma-resv.h>
> > > +#include <linux/dma-fence-chain.h>
> > > +#include <linux/dma-fence-array.h>
> > >  #include <linux/export.h>
> > >  #include <linux/mm.h>
> > >  #include <linux/sched/mm.h>
> > > @@ -49,6 +51,19 @@
> > >   * write-side updates.
> > >   */
> > >
> > > +/**
> > > + * dma_fence_deep_dive_for_each - deep dive into the fence containers
> > > + * @fence: resulting fence
> > > + * @chain: variable for a dma_fence_chain
> > > + * @index: index into a dma_fence_array
> > > + * @head: starting point
> > > + *
> > > + * Helper to deep dive into the fence containers for flattening them.
> > > + */
> > > +#define dma_fence_deep_dive_for_each(fence, chain, index, head)      \
> > > +     dma_fence_chain_for_each(chain, head)                   \
> > > +             dma_fence_array_for_each(fence, index, chain)
> >
> > Since this is is just internal helper in the .c file we generally don't
> > document it. Maybe small comment if you feel it's worth it.
> 
> Sure, I can write LESS documentation. :-P
> 
> > > +
> > >  DEFINE_WD_CLASS(reservation_ww_class);
> > >  EXPORT_SYMBOL(reservation_ww_class);
> > >
> > > @@ -517,6 +532,113 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
> > >  }
> > >  EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
> > >
> > > +/**
> > > + * dma_resv_get_singleton - get a single fence for the dma_resv object
> >
> > Name doesn't match here.
> 
> Fixed.
> 
> > > + * @obj: the reservation object
> > > + * @extra: extra fence to add to the resulting array
> > > + * @result: resulting dma_fence
> > > + *
> > > + * Get a single fence representing all unsignaled fences in the dma_resv object
> > > + * plus the given extra fence. If we got only one fence return a new
> > > + * reference to that, otherwise return a dma_fence_array object.
> > > + *
> > > + * RETURNS
> > > + * Returns -NOMEM if allocations fail, zero otherwise.
> >
> > Kernel often encodes this in ERR_PTR so that you don't have to pass a
> > pointer to store the result. Would feel more kerenl-y I think that way. So
> > no result parameter, and on alloc failure you'd return
> 
> Done.
> 
> >         return ERR_PTR(-ENOMEM);
> >
> > > + */
> > > +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
> >
> > tbh the _rcu here is confusing. I think _unlocked is the better suffix,
> > maybe we should rename dma_resv_get_fences_rcu too for consistency. The
> > rcu-ness of the lookup isn't leaked to callers at all, so no point giving
> > them a panic.
> 
> I can make that change.  I'll also include a patch in the next re-send
> that renames all the _rcu helpers to _unlocked for consistency.

Maybe double-check with Christian König whether he's on board with this
bikeshed, but the current _rcu postfix we have in some dma_resv functions
really confuses me.
-Daniel

> 
> --Jason
> 
> > > +                            struct dma_fence **result)
> > > +{
> > > +     struct dma_fence **resv_fences, *fence, *chain, **fences;
> > > +     struct dma_fence_array *array;
> > > +     unsigned int num_resv_fences, num_fences;
> > > +     unsigned int ret, i, j;
> > > +
> > > +     ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     num_fences = 0;
> > > +     *result = NULL;
> > > +
> > > +     if (num_resv_fences == 0 && !extra)
> > > +             return 0;
> > > +
> > > +     for (i = 0; i < num_resv_fences; ++i) {
> > > +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> > > +                     if (dma_fence_is_signaled(fence))
> > > +                             continue;
> > > +
> > > +                     *result = fence;
> > > +                     ++num_fences;
> > > +             }
> > > +     }
> > > +
> > > +     if (extra) {
> > > +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> > > +                     if (dma_fence_is_signaled(fence))
> > > +                             continue;
> > > +
> > > +                     *result = fence;
> > > +                     ++num_fences;
> > > +             }
> > > +     }
> > > +
> > > +     if (num_fences <= 1) {
> > > +             *result = dma_fence_get(*result);
> > > +             goto put_resv_fences;
> > > +     }
> > > +
> > > +     fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
> > > +                            GFP_KERNEL);
> > > +     if (!fences) {
> > > +             *result = NULL;
> > > +             ret = -ENOMEM;
> > > +             goto put_resv_fences;
> > > +     }
> > > +
> > > +     num_fences = 0;
> > > +     for (i = 0; i < num_resv_fences; ++i) {
> > > +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
> > > +                     if (!dma_fence_is_signaled(fence))
> > > +                             fences[num_fences++] = dma_fence_get(fence);
> > > +             }
> > > +     }
> > > +
> > > +     if (extra) {
> > > +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
> > > +                     if (dma_fence_is_signaled(fence))
> > > +                             fences[num_fences++] = dma_fence_get(fence);
> > > +             }
> > > +     }
> > > +
> > > +     if (num_fences <= 1) {
> > > +             *result = num_fences ? fences[0] : NULL;
> > > +             kfree(fences);
> > > +             goto put_resv_fences;
> > > +     }
> > > +
> > > +     array = dma_fence_array_create(num_fences, fences,
> > > +                                    dma_fence_context_alloc(1),
> > > +                                    1, false);
> > > +     if (array) {
> > > +             *result = &array->base;
> > > +     } else {
> > > +             *result = NULL;
> > > +             while (num_fences--)
> > > +                     dma_fence_put(fences[num_fences]);
> > > +             kfree(fences);
> > > +             ret = -ENOMEM;
> > > +     }
> > > +
> > > +put_resv_fences:
> > > +     while (num_resv_fences--)
> > > +             dma_fence_put(resv_fences[num_resv_fences]);
> > > +     kfree(resv_fences);
> > > +
> > > +     return ret;
> > > +}
> > > +EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
> >
> > With the nits addressed:
> >
> > Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> >
> > > +
> > >  /**
> > >   * dma_resv_wait_timeout_rcu - Wait on reservation's objects
> > >   * shared and/or exclusive fences.
> > > diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
> > > index d44a77e8a7e34..d49ca263e78b4 100644
> > > --- a/include/linux/dma-resv.h
> > > +++ b/include/linux/dma-resv.h
> > > @@ -285,6 +285,9 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
> > >
> > >  int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
> > >
> > > +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
> > > +                            struct dma_fence **result);
> > > +
> > >  long dma_resv_wait_timeout_rcu(struct dma_resv *obj, bool wait_all, bool intr,
> > >                              unsigned long timeout);
> > >
> > > --
> > > 2.31.1
> > >
> >
> > --
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > http://blog.ffwll.ch
Christian König May 25, 2021, 3:50 p.m. UTC | #6
Am 25.05.21 um 14:38 schrieb Daniel Vetter:
> On Mon, May 24, 2021 at 03:04:35PM -0500, Jason Ekstrand wrote:
>> On Fri, May 21, 2021 at 12:48 PM Daniel Vetter <daniel@ffwll.ch> wrote:
>>> On Thu, May 20, 2021 at 02:00:05PM -0500, Jason Ekstrand wrote:
>>>> Add a helper function to get a single fence representing
>>>> all fences in a dma_resv object.
>>>>
>>>> This fence is either the only one in the object or all not
>>>> signaled fences of the object in a flatted out dma_fence_array.
>>>>
>>>> v2 (Jason Ekstrand):
>>>>   - Take reference of fences both for creating the dma_fence_array and in
>>>>     the case where we return one fence.
>>>>   - Handle the case where dma_resv_get_list() returns NULL
>>>>
>>>> v3 (Jason Ekstrand):
>>>>   - Add an _rcu suffix because it is read-only
>>>>   - Rewrite to use dma_resv_get_fences_rcu so it's RCU-safe
>>>>   - Add an EXPORT_SYMBOL_GPL declaration
>>>>   - Re-author the patch to Jason since very little is left of Christian
>>>>     König's original patch
>>>>   - Remove the extra fence argument
>>>>
>>>> v4 (Jason Ekstrand):
>>>>   - Restore the extra fence argument
>>>>
>>>> Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
>>>>
>>>> get_singleton
>>> Spurious thing here.
>> Fixed.
>>
>>>> ---
>>>>   drivers/dma-buf/dma-resv.c | 122 +++++++++++++++++++++++++++++++++++++
>>>>   include/linux/dma-resv.h   |   3 +
>>>>   2 files changed, 125 insertions(+)
>>>>
>>>> diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
>>>> index 6ddbeb5dfbf65..25995fc15c370 100644
>>>> --- a/drivers/dma-buf/dma-resv.c
>>>> +++ b/drivers/dma-buf/dma-resv.c
>>>> @@ -33,6 +33,8 @@
>>>>    */
>>>>
>>>>   #include <linux/dma-resv.h>
>>>> +#include <linux/dma-fence-chain.h>
>>>> +#include <linux/dma-fence-array.h>
>>>>   #include <linux/export.h>
>>>>   #include <linux/mm.h>
>>>>   #include <linux/sched/mm.h>
>>>> @@ -49,6 +51,19 @@
>>>>    * write-side updates.
>>>>    */
>>>>
>>>> +/**
>>>> + * dma_fence_deep_dive_for_each - deep dive into the fence containers
>>>> + * @fence: resulting fence
>>>> + * @chain: variable for a dma_fence_chain
>>>> + * @index: index into a dma_fence_array
>>>> + * @head: starting point
>>>> + *
>>>> + * Helper to deep dive into the fence containers for flattening them.
>>>> + */
>>>> +#define dma_fence_deep_dive_for_each(fence, chain, index, head)      \
>>>> +     dma_fence_chain_for_each(chain, head)                   \
>>>> +             dma_fence_array_for_each(fence, index, chain)
>>> Since this is is just internal helper in the .c file we generally don't
>>> document it. Maybe small comment if you feel it's worth it.
>> Sure, I can write LESS documentation. :-P
>>
>>>> +
>>>>   DEFINE_WD_CLASS(reservation_ww_class);
>>>>   EXPORT_SYMBOL(reservation_ww_class);
>>>>
>>>> @@ -517,6 +532,113 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
>>>>   }
>>>>   EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
>>>>
>>>> +/**
>>>> + * dma_resv_get_singleton - get a single fence for the dma_resv object
>>> Name doesn't match here.
>> Fixed.
>>
>>>> + * @obj: the reservation object
>>>> + * @extra: extra fence to add to the resulting array
>>>> + * @result: resulting dma_fence
>>>> + *
>>>> + * Get a single fence representing all unsignaled fences in the dma_resv object
>>>> + * plus the given extra fence. If we got only one fence return a new
>>>> + * reference to that, otherwise return a dma_fence_array object.
>>>> + *
>>>> + * RETURNS
>>>> + * Returns -NOMEM if allocations fail, zero otherwise.
>>> Kernel often encodes this in ERR_PTR so that you don't have to pass a
>>> pointer to store the result. Would feel more kerenl-y I think that way. So
>>> no result parameter, and on alloc failure you'd return
>> Done.
>>
>>>          return ERR_PTR(-ENOMEM);
>>>
>>>> + */
>>>> +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
>>> tbh the _rcu here is confusing. I think _unlocked is the better suffix,
>>> maybe we should rename dma_resv_get_fences_rcu too for consistency. The
>>> rcu-ness of the lookup isn't leaked to callers at all, so no point giving
>>> them a panic.
>> I can make that change.  I'll also include a patch in the next re-send
>> that renames all the _rcu helpers to _unlocked for consistency.
> Maybe double-check with Christian König whether he's on board with this
> bikeshed, but the current _rcu postfix we have in some dma_resv functions
> really confuses me.

Well I would say yes please :)

The _rcu postfix was something I was always wondering about as well.

Christian.

> -Daniel
>
>> --Jason
>>
>>>> +                            struct dma_fence **result)
>>>> +{
>>>> +     struct dma_fence **resv_fences, *fence, *chain, **fences;
>>>> +     struct dma_fence_array *array;
>>>> +     unsigned int num_resv_fences, num_fences;
>>>> +     unsigned int ret, i, j;
>>>> +
>>>> +     ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     num_fences = 0;
>>>> +     *result = NULL;
>>>> +
>>>> +     if (num_resv_fences == 0 && !extra)
>>>> +             return 0;
>>>> +
>>>> +     for (i = 0; i < num_resv_fences; ++i) {
>>>> +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
>>>> +                     if (dma_fence_is_signaled(fence))
>>>> +                             continue;
>>>> +
>>>> +                     *result = fence;
>>>> +                     ++num_fences;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (extra) {
>>>> +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
>>>> +                     if (dma_fence_is_signaled(fence))
>>>> +                             continue;
>>>> +
>>>> +                     *result = fence;
>>>> +                     ++num_fences;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (num_fences <= 1) {
>>>> +             *result = dma_fence_get(*result);
>>>> +             goto put_resv_fences;
>>>> +     }
>>>> +
>>>> +     fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
>>>> +                            GFP_KERNEL);
>>>> +     if (!fences) {
>>>> +             *result = NULL;
>>>> +             ret = -ENOMEM;
>>>> +             goto put_resv_fences;
>>>> +     }
>>>> +
>>>> +     num_fences = 0;
>>>> +     for (i = 0; i < num_resv_fences; ++i) {
>>>> +             dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
>>>> +                     if (!dma_fence_is_signaled(fence))
>>>> +                             fences[num_fences++] = dma_fence_get(fence);
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (extra) {
>>>> +             dma_fence_deep_dive_for_each(fence, chain, j, extra) {
>>>> +                     if (dma_fence_is_signaled(fence))
>>>> +                             fences[num_fences++] = dma_fence_get(fence);
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (num_fences <= 1) {
>>>> +             *result = num_fences ? fences[0] : NULL;
>>>> +             kfree(fences);
>>>> +             goto put_resv_fences;
>>>> +     }
>>>> +
>>>> +     array = dma_fence_array_create(num_fences, fences,
>>>> +                                    dma_fence_context_alloc(1),
>>>> +                                    1, false);
>>>> +     if (array) {
>>>> +             *result = &array->base;
>>>> +     } else {
>>>> +             *result = NULL;
>>>> +             while (num_fences--)
>>>> +                     dma_fence_put(fences[num_fences]);
>>>> +             kfree(fences);
>>>> +             ret = -ENOMEM;
>>>> +     }
>>>> +
>>>> +put_resv_fences:
>>>> +     while (num_resv_fences--)
>>>> +             dma_fence_put(resv_fences[num_resv_fences]);
>>>> +     kfree(resv_fences);
>>>> +
>>>> +     return ret;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
>>> With the nits addressed:
>>>
>>> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>>>
>>>> +
>>>>   /**
>>>>    * dma_resv_wait_timeout_rcu - Wait on reservation's objects
>>>>    * shared and/or exclusive fences.
>>>> diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
>>>> index d44a77e8a7e34..d49ca263e78b4 100644
>>>> --- a/include/linux/dma-resv.h
>>>> +++ b/include/linux/dma-resv.h
>>>> @@ -285,6 +285,9 @@ int dma_resv_get_fences_rcu(struct dma_resv *obj,
>>>>
>>>>   int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
>>>>
>>>> +int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
>>>> +                            struct dma_fence **result);
>>>> +
>>>>   long dma_resv_wait_timeout_rcu(struct dma_resv *obj, bool wait_all, bool intr,
>>>>                               unsigned long timeout);
>>>>
>>>> --
>>>> 2.31.1
>>>>
>>> --
>>> Daniel Vetter
>>> Software Engineer, Intel Corporation
>>> https://nam11.safelinks.protection.outlook.com/?url=http%3A%2F%2Fblog.ffwll.ch%2F&amp;data=04%7C01%7Cchristian.koenig%40amd.com%7C4bfa8c05efd842e47f3b08d91f7a0121%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637575431134167291%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=8y%2BHz5GldUZK6Ey%2FuZUQUMg%2BObKticrSVX2gx3NBDOw%3D&amp;reserved=0
diff mbox series

Patch

diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
index 6ddbeb5dfbf65..25995fc15c370 100644
--- a/drivers/dma-buf/dma-resv.c
+++ b/drivers/dma-buf/dma-resv.c
@@ -33,6 +33,8 @@ 
  */
 
 #include <linux/dma-resv.h>
+#include <linux/dma-fence-chain.h>
+#include <linux/dma-fence-array.h>
 #include <linux/export.h>
 #include <linux/mm.h>
 #include <linux/sched/mm.h>
@@ -49,6 +51,19 @@ 
  * write-side updates.
  */
 
+/**
+ * dma_fence_deep_dive_for_each - deep dive into the fence containers
+ * @fence: resulting fence
+ * @chain: variable for a dma_fence_chain
+ * @index: index into a dma_fence_array
+ * @head: starting point
+ *
+ * Helper to deep dive into the fence containers for flattening them.
+ */
+#define dma_fence_deep_dive_for_each(fence, chain, index, head)	\
+	dma_fence_chain_for_each(chain, head)			\
+		dma_fence_array_for_each(fence, index, chain)
+
 DEFINE_WD_CLASS(reservation_ww_class);
 EXPORT_SYMBOL(reservation_ww_class);
 
@@ -517,6 +532,113 @@  int dma_resv_get_fences_rcu(struct dma_resv *obj,
 }
 EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
 
+/**
+ * dma_resv_get_singleton - get a single fence for the dma_resv object
+ * @obj: the reservation object
+ * @extra: extra fence to add to the resulting array
+ * @result: resulting dma_fence
+ *
+ * Get a single fence representing all unsignaled fences in the dma_resv object
+ * plus the given extra fence. If we got only one fence return a new
+ * reference to that, otherwise return a dma_fence_array object.
+ *
+ * RETURNS
+ * Returns -NOMEM if allocations fail, zero otherwise.
+ */
+int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
+			       struct dma_fence **result)
+{
+	struct dma_fence **resv_fences, *fence, *chain, **fences;
+	struct dma_fence_array *array;
+	unsigned int num_resv_fences, num_fences;
+	unsigned int ret, i, j;
+
+	ret = dma_resv_get_fences_rcu(obj, NULL, &num_resv_fences, &resv_fences);
+	if (ret)
+		return ret;
+
+	num_fences = 0;
+	*result = NULL;
+
+	if (num_resv_fences == 0 && !extra)
+		return 0;
+
+	for (i = 0; i < num_resv_fences; ++i) {
+		dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
+			if (dma_fence_is_signaled(fence))
+				continue;
+
+			*result = fence;
+			++num_fences;
+		}
+	}
+
+	if (extra) {
+		dma_fence_deep_dive_for_each(fence, chain, j, extra) {
+			if (dma_fence_is_signaled(fence))
+				continue;
+
+			*result = fence;
+			++num_fences;
+		}
+	}
+
+	if (num_fences <= 1) {
+		*result = dma_fence_get(*result);
+		goto put_resv_fences;
+	}
+
+	fences = kmalloc_array(num_fences, sizeof(struct dma_fence*),
+			       GFP_KERNEL);
+	if (!fences) {
+		*result = NULL;
+		ret = -ENOMEM;
+		goto put_resv_fences;
+	}
+
+	num_fences = 0;
+	for (i = 0; i < num_resv_fences; ++i) {
+		dma_fence_deep_dive_for_each(fence, chain, j, resv_fences[i]) {
+			if (!dma_fence_is_signaled(fence))
+				fences[num_fences++] = dma_fence_get(fence);
+		}
+	}
+
+	if (extra) {
+		dma_fence_deep_dive_for_each(fence, chain, j, extra) {
+			if (dma_fence_is_signaled(fence))
+				fences[num_fences++] = dma_fence_get(fence);
+		}
+	}
+
+	if (num_fences <= 1) {
+		*result = num_fences ? fences[0] : NULL;
+		kfree(fences);
+		goto put_resv_fences;
+	}
+
+	array = dma_fence_array_create(num_fences, fences,
+				       dma_fence_context_alloc(1),
+				       1, false);
+	if (array) {
+		*result = &array->base;
+	} else {
+		*result = NULL;
+		while (num_fences--)
+			dma_fence_put(fences[num_fences]);
+		kfree(fences);
+		ret = -ENOMEM;
+	}
+
+put_resv_fences:
+	while (num_resv_fences--)
+		dma_fence_put(resv_fences[num_resv_fences]);
+	kfree(resv_fences);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_resv_get_singleton_rcu);
+
 /**
  * dma_resv_wait_timeout_rcu - Wait on reservation's objects
  * shared and/or exclusive fences.
diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
index d44a77e8a7e34..d49ca263e78b4 100644
--- a/include/linux/dma-resv.h
+++ b/include/linux/dma-resv.h
@@ -285,6 +285,9 @@  int dma_resv_get_fences_rcu(struct dma_resv *obj,
 
 int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
 
+int dma_resv_get_singleton_rcu(struct dma_resv *obj, struct dma_fence *extra,
+			       struct dma_fence **result);
+
 long dma_resv_wait_timeout_rcu(struct dma_resv *obj, bool wait_all, bool intr,
 			       unsigned long timeout);