2014-07-01 14:57:31 +04:00
/*
2016-08-11 18:26:42 +03:00
* Sync File validation framework and debug information
2014-07-01 14:57:31 +04:00
*
* Copyright ( C ) 2012 Google , Inc .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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/debugfs.h>
2016-05-31 22:59:12 +03:00
# include "sync_debug.h"
2014-07-01 14:57:31 +04:00
2016-01-21 15:49:17 +03:00
static struct dentry * dbgfs ;
2014-07-01 14:57:31 +04:00
static LIST_HEAD ( sync_timeline_list_head ) ;
static DEFINE_SPINLOCK ( sync_timeline_list_lock ) ;
2016-01-21 15:49:19 +03:00
static LIST_HEAD ( sync_file_list_head ) ;
static DEFINE_SPINLOCK ( sync_file_list_lock ) ;
2014-07-01 14:57:31 +04:00
void sync_timeline_debug_add ( struct sync_timeline * obj )
{
unsigned long flags ;
spin_lock_irqsave ( & sync_timeline_list_lock , flags ) ;
list_add_tail ( & obj - > sync_timeline_list , & sync_timeline_list_head ) ;
spin_unlock_irqrestore ( & sync_timeline_list_lock , flags ) ;
}
void sync_timeline_debug_remove ( struct sync_timeline * obj )
{
unsigned long flags ;
spin_lock_irqsave ( & sync_timeline_list_lock , flags ) ;
list_del ( & obj - > sync_timeline_list ) ;
spin_unlock_irqrestore ( & sync_timeline_list_lock , flags ) ;
}
2016-01-21 15:49:19 +03:00
void sync_file_debug_add ( struct sync_file * sync_file )
2014-07-01 14:57:31 +04:00
{
unsigned long flags ;
2016-01-21 15:49:19 +03:00
spin_lock_irqsave ( & sync_file_list_lock , flags ) ;
list_add_tail ( & sync_file - > sync_file_list , & sync_file_list_head ) ;
spin_unlock_irqrestore ( & sync_file_list_lock , flags ) ;
2014-07-01 14:57:31 +04:00
}
2016-01-21 15:49:19 +03:00
void sync_file_debug_remove ( struct sync_file * sync_file )
2014-07-01 14:57:31 +04:00
{
unsigned long flags ;
2016-01-21 15:49:19 +03:00
spin_lock_irqsave ( & sync_file_list_lock , flags ) ;
list_del ( & sync_file - > sync_file_list ) ;
spin_unlock_irqrestore ( & sync_file_list_lock , flags ) ;
2014-07-01 14:57:31 +04:00
}
static const char * sync_status_str ( int status )
{
2017-01-04 17:12:21 +03:00
if ( status < 0 )
return " error " ;
2014-07-12 23:55:56 +04:00
if ( status > 0 )
2017-01-04 17:12:21 +03:00
return " signaled " ;
2014-07-12 23:55:56 +04:00
2017-01-04 17:12:21 +03:00
return " active " ;
2014-07-01 14:57:31 +04:00
}
2016-10-25 15:00:45 +03:00
static void sync_print_fence ( struct seq_file * s ,
struct dma_fence * fence , bool show )
2014-07-01 14:57:31 +04:00
{
2016-10-25 15:00:45 +03:00
struct sync_timeline * parent = dma_fence_parent ( fence ) ;
2017-01-04 17:12:21 +03:00
int status ;
2014-07-01 14:57:31 +04:00
2017-01-04 17:12:21 +03:00
status = dma_fence_get_status_locked ( fence ) ;
2014-07-01 14:57:31 +04:00
2016-01-21 15:49:21 +03:00
seq_printf ( s , " %s%sfence %s " ,
show ? parent - > name : " " ,
show ? " _ " : " " ,
2014-07-01 14:57:31 +04:00
sync_status_str ( status ) ) ;
2017-01-04 17:12:21 +03:00
if ( status ) {
2014-12-24 18:33:02 +03:00
struct timespec64 ts64 =
2016-01-21 15:49:21 +03:00
ktime_to_timespec64 ( fence - > timestamp ) ;
2014-07-12 23:55:56 +04:00
2014-10-26 16:50:16 +03:00
seq_printf ( s , " @%lld.%09ld " , ( s64 ) ts64 . tv_sec , ts64 . tv_nsec ) ;
2014-07-01 14:57:31 +04:00
}
2016-05-31 22:59:02 +03:00
if ( fence - > ops - > timeline_value_str & &
2016-01-21 15:49:21 +03:00
fence - > ops - > fence_value_str ) {
2014-07-01 14:57:31 +04:00
char value [ 64 ] ;
2015-12-11 16:11:49 +03:00
bool success ;
2014-07-12 23:55:56 +04:00
2016-01-21 15:49:21 +03:00
fence - > ops - > fence_value_str ( fence , value , sizeof ( value ) ) ;
2015-12-11 16:11:49 +03:00
success = strlen ( value ) ;
2016-05-31 22:59:02 +03:00
if ( success ) {
2015-12-11 16:11:49 +03:00
seq_printf ( s , " : %s " , value ) ;
2016-01-21 15:49:21 +03:00
fence - > ops - > timeline_value_str ( fence , value ,
sizeof ( value ) ) ;
2015-12-11 16:11:49 +03:00
if ( strlen ( value ) )
seq_printf ( s , " / %s " , value ) ;
2014-07-01 14:57:31 +04:00
}
}
seq_puts ( s , " \n " ) ;
}
static void sync_print_obj ( struct seq_file * s , struct sync_timeline * obj )
{
struct list_head * pos ;
unsigned long flags ;
2016-05-31 22:59:11 +03:00
seq_printf ( s , " %s: %d \n " , obj - > name , obj - > value ) ;
2014-07-01 14:57:31 +04:00
spin_lock_irqsave ( & obj - > child_list_lock , flags ) ;
list_for_each ( pos , & obj - > child_list_head ) {
2016-05-31 22:59:04 +03:00
struct sync_pt * pt =
container_of ( pos , struct sync_pt , child_list ) ;
sync_print_fence ( s , & pt - > base , false ) ;
2014-07-01 14:57:31 +04:00
}
spin_unlock_irqrestore ( & obj - > child_list_lock , flags ) ;
}
2016-01-21 15:49:19 +03:00
static void sync_print_sync_file ( struct seq_file * s ,
struct sync_file * sync_file )
2016-01-21 15:49:21 +03:00
{
2014-07-01 14:57:31 +04:00
int i ;
2016-01-21 15:49:19 +03:00
seq_printf ( s , " [%p] %s: %s \n " , sync_file , sync_file - > name ,
2017-01-04 17:12:21 +03:00
sync_status_str ( dma_fence_get_status ( sync_file - > fence ) ) ) ;
2014-07-01 14:57:31 +04:00
2016-10-25 15:00:45 +03:00
if ( dma_fence_is_array ( sync_file - > fence ) ) {
struct dma_fence_array * array = to_dma_fence_array ( sync_file - > fence ) ;
2016-08-05 16:39:35 +03:00
for ( i = 0 ; i < array - > num_fences ; + + i )
sync_print_fence ( s , array - > fences [ i ] , true ) ;
} else {
sync_print_fence ( s , sync_file - > fence , true ) ;
}
2016-01-21 15:49:21 +03:00
}
2014-07-01 14:57:31 +04:00
static int sync_debugfs_show ( struct seq_file * s , void * unused )
{
unsigned long flags ;
struct list_head * pos ;
seq_puts ( s , " objs: \n -------------- \n " ) ;
spin_lock_irqsave ( & sync_timeline_list_lock , flags ) ;
list_for_each ( pos , & sync_timeline_list_head ) {
struct sync_timeline * obj =
container_of ( pos , struct sync_timeline ,
sync_timeline_list ) ;
sync_print_obj ( s , obj ) ;
seq_puts ( s , " \n " ) ;
}
spin_unlock_irqrestore ( & sync_timeline_list_lock , flags ) ;
seq_puts ( s , " fences: \n -------------- \n " ) ;
2016-01-21 15:49:19 +03:00
spin_lock_irqsave ( & sync_file_list_lock , flags ) ;
list_for_each ( pos , & sync_file_list_head ) {
struct sync_file * sync_file =
container_of ( pos , struct sync_file , sync_file_list ) ;
2014-07-01 14:57:31 +04:00
2016-01-21 15:49:19 +03:00
sync_print_sync_file ( s , sync_file ) ;
2014-07-01 14:57:31 +04:00
seq_puts ( s , " \n " ) ;
}
2016-01-21 15:49:19 +03:00
spin_unlock_irqrestore ( & sync_file_list_lock , flags ) ;
2014-07-01 14:57:31 +04:00
return 0 ;
}
2016-01-21 15:49:17 +03:00
static int sync_info_debugfs_open ( struct inode * inode , struct file * file )
2014-07-01 14:57:31 +04:00
{
return single_open ( file , sync_debugfs_show , inode - > i_private ) ;
}
2016-01-21 15:49:17 +03:00
static const struct file_operations sync_info_debugfs_fops = {
. open = sync_info_debugfs_open ,
2014-07-01 14:57:31 +04:00
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static __init int sync_debugfs_init ( void )
{
2016-01-21 15:49:17 +03:00
dbgfs = debugfs_create_dir ( " sync " , NULL ) ;
2016-05-27 21:03:54 +03:00
/*
* The debugfs files won ' t ever get removed and thus , there is
* no need to protect it against removal races . The use of
* debugfs_create_file_unsafe ( ) is actually safe here .
*/
debugfs_create_file_unsafe ( " info " , 0444 , dbgfs , NULL ,
& sync_info_debugfs_fops ) ;
debugfs_create_file_unsafe ( " sw_sync " , 0644 , dbgfs , NULL ,
& sw_sync_debugfs_fops ) ;
2016-01-21 15:49:17 +03:00
2014-07-01 14:57:31 +04:00
return 0 ;
}
late_initcall ( sync_debugfs_init ) ;
# define DUMP_CHUNK 256
static char sync_dump_buf [ 64 * 1024 ] ;
void sync_dump ( void )
{
struct seq_file s = {
. buf = sync_dump_buf ,
. size = sizeof ( sync_dump_buf ) - 1 ,
} ;
int i ;
sync_debugfs_show ( & s , NULL ) ;
for ( i = 0 ; i < s . count ; i + = DUMP_CHUNK ) {
if ( ( s . count - i ) > DUMP_CHUNK ) {
char c = s . buf [ i + DUMP_CHUNK ] ;
2014-07-12 23:55:56 +04:00
2014-07-01 14:57:31 +04:00
s . buf [ i + DUMP_CHUNK ] = 0 ;
pr_cont ( " %s " , s . buf + i ) ;
s . buf [ i + DUMP_CHUNK ] = c ;
} else {
s . buf [ s . count ] = 0 ;
pr_cont ( " %s " , s . buf + i ) ;
}
}
}