2016-06-01 16:10:03 +03:00
/*
* fence - array : aggregate fences to be waited together
*
* Copyright ( C ) 2016 Collabora Ltd
* Copyright ( C ) 2016 Advanced Micro Devices , Inc .
* Authors :
* Gustavo Padovan < gustavo @ padovan . org >
* Christian König < christian . koenig @ amd . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
# include <linux/export.h>
# include <linux/slab.h>
# include <linux/fence-array.h>
static void fence_array_cb_func ( struct fence * f , struct fence_cb * cb ) ;
static const char * fence_array_get_driver_name ( struct fence * fence )
{
return " fence_array " ;
}
static const char * fence_array_get_timeline_name ( struct fence * fence )
{
return " unbound " ;
}
static void fence_array_cb_func ( struct fence * f , struct fence_cb * cb )
{
struct fence_array_cb * array_cb =
container_of ( cb , struct fence_array_cb , cb ) ;
struct fence_array * array = array_cb - > array ;
if ( atomic_dec_and_test ( & array - > num_pending ) )
fence_signal ( & array - > base ) ;
2016-06-01 16:10:04 +03:00
fence_put ( & array - > base ) ;
2016-06-01 16:10:03 +03:00
}
static bool fence_array_enable_signaling ( struct fence * fence )
{
struct fence_array * array = to_fence_array ( fence ) ;
struct fence_array_cb * cb = ( void * ) ( & array [ 1 ] ) ;
unsigned i ;
for ( i = 0 ; i < array - > num_fences ; + + i ) {
cb [ i ] . array = array ;
2016-06-01 16:10:04 +03:00
/*
* As we may report that the fence is signaled before all
* callbacks are complete , we need to take an additional
* reference count on the array so that we do not free it too
* early . The core fence handling will only hold the reference
* until we signal the array as complete ( but that is now
* insufficient ) .
*/
fence_get ( & array - > base ) ;
2016-06-01 16:10:03 +03:00
if ( fence_add_callback ( array - > fences [ i ] , & cb [ i ] . cb ,
2016-06-01 16:10:04 +03:00
fence_array_cb_func ) ) {
fence_put ( & array - > base ) ;
2016-06-01 16:10:03 +03:00
if ( atomic_dec_and_test ( & array - > num_pending ) )
return false ;
2016-06-01 16:10:04 +03:00
}
2016-06-01 16:10:03 +03:00
}
return true ;
}
static bool fence_array_signaled ( struct fence * fence )
{
struct fence_array * array = to_fence_array ( fence ) ;
2016-06-01 16:10:04 +03:00
return atomic_read ( & array - > num_pending ) < = 0 ;
2016-06-01 16:10:03 +03:00
}
static void fence_array_release ( struct fence * fence )
{
struct fence_array * array = to_fence_array ( fence ) ;
unsigned i ;
for ( i = 0 ; i < array - > num_fences ; + + i )
fence_put ( array - > fences [ i ] ) ;
kfree ( array - > fences ) ;
fence_free ( fence ) ;
}
const struct fence_ops fence_array_ops = {
. get_driver_name = fence_array_get_driver_name ,
. get_timeline_name = fence_array_get_timeline_name ,
. enable_signaling = fence_array_enable_signaling ,
. signaled = fence_array_signaled ,
. wait = fence_default_wait ,
. release = fence_array_release ,
} ;
/**
* fence_array_create - Create a custom fence array
2016-06-01 16:10:04 +03:00
* @ num_fences : [ in ] number of fences to add in the array
* @ fences : [ in ] array containing the fences
* @ context : [ in ] fence context to use
* @ seqno : [ in ] sequence number to use
* @ signal_on_any [ in ] signal on any fence in the array
2016-06-01 16:10:03 +03:00
*
* Allocate a fence_array object and initialize the base fence with fence_init ( ) .
* In case of error it returns NULL .
*
* The caller should allocte the fences array with num_fences size
* and fill it with the fences it wants to add to the object . Ownership of this
* array is take and fence_put ( ) is used on each fence on release .
2016-06-01 16:10:04 +03:00
*
* If @ signal_on_any is true the fence array signals if any fence in the array
* signals , otherwise it signals when all fences in the array signal .
2016-06-01 16:10:03 +03:00
*/
struct fence_array * fence_array_create ( int num_fences , struct fence * * fences ,
2016-06-01 16:10:04 +03:00
u64 context , unsigned seqno ,
bool signal_on_any )
2016-06-01 16:10:03 +03:00
{
struct fence_array * array ;
size_t size = sizeof ( * array ) ;
/* Allocate the callback structures behind the array. */
size + = num_fences * sizeof ( struct fence_array_cb ) ;
array = kzalloc ( size , GFP_KERNEL ) ;
if ( ! array )
return NULL ;
spin_lock_init ( & array - > lock ) ;
fence_init ( & array - > base , & fence_array_ops , & array - > lock ,
context , seqno ) ;
array - > num_fences = num_fences ;
2016-06-01 16:10:04 +03:00
atomic_set ( & array - > num_pending , signal_on_any ? 1 : num_fences ) ;
2016-06-01 16:10:03 +03:00
array - > fences = fences ;
return array ;
}
EXPORT_SYMBOL ( fence_array_create ) ;