diff mbox

[RFC,v2,2/3] dma-buf/fence-array: add fence_array_teardown()

Message ID 1467055762-25881-3-git-send-email-gustavo@padovan.org (mailing list archive)
State New, archived
Headers show

Commit Message

Gustavo Padovan June 27, 2016, 7:29 p.m. UTC
From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

As the array of fence callbacks held by an active struct fence_array
each has a reference to the struct fence_array, when the owner of the
fence_array is freed it must dispose of the callback references before
it can free the fence_array. This can not happen simply during
fence_release() because of the extra references and so we need a new
function to run before the final fence_put().

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Christian König <christian.koenig@amd.com>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
---
 drivers/dma-buf/fence-array.c | 25 +++++++++++++++++++++++++
 include/linux/fence-array.h   |  1 +
 2 files changed, 26 insertions(+)

Comments

Christian König June 28, 2016, 9:56 a.m. UTC | #1
Am 27.06.2016 um 21:29 schrieb Gustavo Padovan:
> From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
>
> As the array of fence callbacks held by an active struct fence_array
> each has a reference to the struct fence_array, when the owner of the
> fence_array is freed it must dispose of the callback references before
> it can free the fence_array. This can not happen simply during
> fence_release() because of the extra references and so we need a new
> function to run before the final fence_put().

As I said previously as well, this is completely superfluous.

The fence array keeps a reference to itself as long as not all callbacks 
are signaled.

So you only need to unregister your callback from the array itself and 
drop your reference when you don't need it any more in the sync file.

Additional to that having a special cleanup function like this strongly 
contradicts the approach of having an implementation independent interface.

So this is a clear NAK from my side.

Regards,
Christian.

>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Christian König <christian.koenig@amd.com>
> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
> ---
>   drivers/dma-buf/fence-array.c | 25 +++++++++++++++++++++++++
>   include/linux/fence-array.h   |  1 +
>   2 files changed, 26 insertions(+)
>
> diff --git a/drivers/dma-buf/fence-array.c b/drivers/dma-buf/fence-array.c
> index a8731c8..8891357 100644
> --- a/drivers/dma-buf/fence-array.c
> +++ b/drivers/dma-buf/fence-array.c
> @@ -142,3 +142,28 @@ struct fence_array *fence_array_create(int num_fences, struct fence **fences,
>   	return array;
>   }
>   EXPORT_SYMBOL(fence_array_create);
> +
> +/**
> + * fence_array_teardown - Teardown and clean up a fence array
> + * @array:	[in]	the fence array to teardown
> + *
> + * This function removes callbacks and extra references to the base fence on
> + * the fence_array for each unsignalled fence in the array. It should be called
> + * before calling fence_put() to remove the last reference on a fence_array,
> + * otherwise the fence won't be released due to extra references holded by the
> + * the fences that still have signalling enabled.
> + */
> +void fence_array_teardown(struct fence_array *array)
> +{
> +	struct fence_array_cb *cb = (void *)(&array[1]);
> +	int i;
> +
> +	for (i = 0; i < array->num_fences; i++) {
> +		if (fence_is_signaled(array->fences[i]))
> +		    continue;
> +
> +		fence_remove_callback(array->fences[i], &cb[i].cb);
> +		fence_put(&array->base);
> +	}
> +}
> +EXPORT_SYMBOL(fence_array_teardown);
> diff --git a/include/linux/fence-array.h b/include/linux/fence-array.h
> index d2e9f40..9f1b923 100644
> --- a/include/linux/fence-array.h
> +++ b/include/linux/fence-array.h
> @@ -79,5 +79,6 @@ static inline struct fence_array *to_fence_array(struct fence *fence)
>   struct fence_array *fence_array_create(int num_fences, struct fence **fences,
>   				       u64 context, unsigned seqno,
>   				       bool signal_on_any);
> +void fence_array_teardown(struct fence_array *array);
>   
>   #endif /* __LINUX_FENCE_ARRAY_H */
Gustavo Padovan June 28, 2016, 2:17 p.m. UTC | #2
2016-06-28 Christian König <christian.koenig@amd.com>:

> Am 27.06.2016 um 21:29 schrieb Gustavo Padovan:
> > From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
> > 
> > As the array of fence callbacks held by an active struct fence_array
> > each has a reference to the struct fence_array, when the owner of the
> > fence_array is freed it must dispose of the callback references before
> > it can free the fence_array. This can not happen simply during
> > fence_release() because of the extra references and so we need a new
> > function to run before the final fence_put().
> 
> As I said previously as well, this is completely superfluous.
> 
> The fence array keeps a reference to itself as long as not all callbacks are
> signaled.
> 
> So you only need to unregister your callback from the array itself and drop
> your reference when you don't need it any more in the sync file.

Exactly, this should be called from sync_file_free() because of the
following use case:

	1. You create 2 sync_file with 1 fence each
	2. Merge both fences, which creates a fence array
	3. Close the sync_file fd without waiting for the fences to
	signal

At this point you leak the fence-array because the final fence_put() 
does not release it because of the extra references from the non
signalled fences so we need to clean up this somehow.

	Gustavo
Christian König June 28, 2016, 3:05 p.m. UTC | #3
Am 28.06.2016 um 16:17 schrieb Gustavo Padovan:
> 2016-06-28 Christian König <christian.koenig@amd.com>:
>
>> Am 27.06.2016 um 21:29 schrieb Gustavo Padovan:
>>> From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
>>>
>>> As the array of fence callbacks held by an active struct fence_array
>>> each has a reference to the struct fence_array, when the owner of the
>>> fence_array is freed it must dispose of the callback references before
>>> it can free the fence_array. This can not happen simply during
>>> fence_release() because of the extra references and so we need a new
>>> function to run before the final fence_put().
>> As I said previously as well, this is completely superfluous.
>>
>> The fence array keeps a reference to itself as long as not all callbacks are
>> signaled.
>>
>> So you only need to unregister your callback from the array itself and drop
>> your reference when you don't need it any more in the sync file.
> Exactly, this should be called from sync_file_free() because of the
> following use case:
>
> 	1. You create 2 sync_file with 1 fence each
> 	2. Merge both fences, which creates a fence array
> 	3. Close the sync_file fd without waiting for the fences to
> 	signal
>
> At this point you leak the fence-array because the final fence_put()
> does not release it because of the extra references from the non
> signalled fences so we need to clean up this somehow.

No, there won't be any leak and you don't need to cleanup anything.

The fences contained in the fence array are already enabled for 
signaling. So they will eventually signal sooner or later and drop the 
reference to the fence array freeing it when the last one finished.

This was done to avoid the need to call fence_remove_callback() all the 
time on the fences in the array because of the warning on the function.

Regards,
Christian.

>
> 	Gustavo
Gustavo Padovan June 28, 2016, 3:17 p.m. UTC | #4
2016-06-28 Christian König <christian.koenig@amd.com>:

> Am 28.06.2016 um 16:17 schrieb Gustavo Padovan:
> > 2016-06-28 Christian König <christian.koenig@amd.com>:
> > 
> > > Am 27.06.2016 um 21:29 schrieb Gustavo Padovan:
> > > > From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
> > > > 
> > > > As the array of fence callbacks held by an active struct fence_array
> > > > each has a reference to the struct fence_array, when the owner of the
> > > > fence_array is freed it must dispose of the callback references before
> > > > it can free the fence_array. This can not happen simply during
> > > > fence_release() because of the extra references and so we need a new
> > > > function to run before the final fence_put().
> > > As I said previously as well, this is completely superfluous.
> > > 
> > > The fence array keeps a reference to itself as long as not all callbacks are
> > > signaled.
> > > 
> > > So you only need to unregister your callback from the array itself and drop
> > > your reference when you don't need it any more in the sync file.
> > Exactly, this should be called from sync_file_free() because of the
> > following use case:
> > 
> > 	1. You create 2 sync_file with 1 fence each
> > 	2. Merge both fences, which creates a fence array
> > 	3. Close the sync_file fd without waiting for the fences to
> > 	signal
> > 
> > At this point you leak the fence-array because the final fence_put()
> > does not release it because of the extra references from the non
> > signalled fences so we need to clean up this somehow.
> 
> No, there won't be any leak and you don't need to cleanup anything.
> 
> The fences contained in the fence array are already enabled for signaling.
> So they will eventually signal sooner or later and drop the reference to the
> fence array freeing it when the last one finished.
> 
> This was done to avoid the need to call fence_remove_callback() all the time
> on the fences in the array because of the warning on the function.

Right. I understand it now.

	Gustavo
diff mbox

Patch

diff --git a/drivers/dma-buf/fence-array.c b/drivers/dma-buf/fence-array.c
index a8731c8..8891357 100644
--- a/drivers/dma-buf/fence-array.c
+++ b/drivers/dma-buf/fence-array.c
@@ -142,3 +142,28 @@  struct fence_array *fence_array_create(int num_fences, struct fence **fences,
 	return array;
 }
 EXPORT_SYMBOL(fence_array_create);
+
+/**
+ * fence_array_teardown - Teardown and clean up a fence array
+ * @array:	[in]	the fence array to teardown
+ *
+ * This function removes callbacks and extra references to the base fence on
+ * the fence_array for each unsignalled fence in the array. It should be called
+ * before calling fence_put() to remove the last reference on a fence_array,
+ * otherwise the fence won't be released due to extra references holded by the
+ * the fences that still have signalling enabled.
+ */
+void fence_array_teardown(struct fence_array *array)
+{
+	struct fence_array_cb *cb = (void *)(&array[1]);
+	int i;
+
+	for (i = 0; i < array->num_fences; i++) {
+		if (fence_is_signaled(array->fences[i]))
+		    continue;
+
+		fence_remove_callback(array->fences[i], &cb[i].cb);
+		fence_put(&array->base);
+	}
+}
+EXPORT_SYMBOL(fence_array_teardown);
diff --git a/include/linux/fence-array.h b/include/linux/fence-array.h
index d2e9f40..9f1b923 100644
--- a/include/linux/fence-array.h
+++ b/include/linux/fence-array.h
@@ -79,5 +79,6 @@  static inline struct fence_array *to_fence_array(struct fence *fence)
 struct fence_array *fence_array_create(int num_fences, struct fence **fences,
 				       u64 context, unsigned seqno,
 				       bool signal_on_any);
+void fence_array_teardown(struct fence_array *array);
 
 #endif /* __LINUX_FENCE_ARRAY_H */