2019-10-04 00:01:49 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Authors :
* Branden Bonaby < brandonbonaby94 @ gmail . com >
*/
# include <linux/hyperv.h>
# include <linux/debugfs.h>
# include <linux/delay.h>
# include <linux/err.h>
# include "hyperv_vmbus.h"
2020-04-03 11:28:45 +03:00
static struct dentry * hv_debug_root ;
2019-10-04 00:01:49 +03:00
static int hv_debugfs_delay_get ( void * data , u64 * val )
{
* val = * ( u32 * ) data ;
return 0 ;
}
static int hv_debugfs_delay_set ( void * data , u64 val )
{
if ( val > 1000 )
return - EINVAL ;
* ( u32 * ) data = val ;
return 0 ;
}
DEFINE_DEBUGFS_ATTRIBUTE ( hv_debugfs_delay_fops , hv_debugfs_delay_get ,
hv_debugfs_delay_set , " %llu \n " ) ;
static int hv_debugfs_state_get ( void * data , u64 * val )
{
* val = * ( bool * ) data ;
return 0 ;
}
static int hv_debugfs_state_set ( void * data , u64 val )
{
if ( val = = 1 )
* ( bool * ) data = true ;
else if ( val = = 0 )
* ( bool * ) data = false ;
else
return - EINVAL ;
return 0 ;
}
DEFINE_DEBUGFS_ATTRIBUTE ( hv_debugfs_state_fops , hv_debugfs_state_get ,
hv_debugfs_state_set , " %llu \n " ) ;
/* Setup delay files to store test values */
static int hv_debug_delay_files ( struct hv_device * dev , struct dentry * root )
{
struct vmbus_channel * channel = dev - > channel ;
char * buffer = " fuzz_test_buffer_interrupt_delay " ;
char * message = " fuzz_test_message_delay " ;
int * buffer_val = & channel - > fuzz_testing_interrupt_delay ;
int * message_val = & channel - > fuzz_testing_message_delay ;
struct dentry * buffer_file , * message_file ;
buffer_file = debugfs_create_file ( buffer , 0644 , root ,
buffer_val ,
& hv_debugfs_delay_fops ) ;
if ( IS_ERR ( buffer_file ) ) {
pr_debug ( " debugfs_hyperv: file %s not created \n " , buffer ) ;
return PTR_ERR ( buffer_file ) ;
}
message_file = debugfs_create_file ( message , 0644 , root ,
message_val ,
& hv_debugfs_delay_fops ) ;
if ( IS_ERR ( message_file ) ) {
pr_debug ( " debugfs_hyperv: file %s not created \n " , message ) ;
return PTR_ERR ( message_file ) ;
}
return 0 ;
}
/* Setup test state value for vmbus device */
static int hv_debug_set_test_state ( struct hv_device * dev , struct dentry * root )
{
struct vmbus_channel * channel = dev - > channel ;
bool * state = & channel - > fuzz_testing_state ;
char * status = " fuzz_test_state " ;
struct dentry * test_state ;
test_state = debugfs_create_file ( status , 0644 , root ,
state ,
& hv_debugfs_state_fops ) ;
if ( IS_ERR ( test_state ) ) {
pr_debug ( " debugfs_hyperv: file %s not created \n " , status ) ;
return PTR_ERR ( test_state ) ;
}
return 0 ;
}
/* Bind hv device to a dentry for debugfs */
static void hv_debug_set_dir_dentry ( struct hv_device * dev , struct dentry * root )
{
if ( hv_debug_root )
dev - > debug_dir = root ;
}
/* Create all test dentry's and names for fuzz testing */
int hv_debug_add_dev_dir ( struct hv_device * dev )
{
const char * device = dev_name ( & dev - > device ) ;
char * delay_name = " delay " ;
struct dentry * delay , * dev_root ;
int ret ;
if ( ! IS_ERR ( hv_debug_root ) ) {
dev_root = debugfs_create_dir ( device , hv_debug_root ) ;
if ( IS_ERR ( dev_root ) ) {
pr_debug ( " debugfs_hyperv: hyperv/%s/ not created \n " ,
device ) ;
return PTR_ERR ( dev_root ) ;
}
hv_debug_set_test_state ( dev , dev_root ) ;
hv_debug_set_dir_dentry ( dev , dev_root ) ;
delay = debugfs_create_dir ( delay_name , dev_root ) ;
if ( IS_ERR ( delay ) ) {
pr_debug ( " debugfs_hyperv: hyperv/%s/%s/ not created \n " ,
device , delay_name ) ;
return PTR_ERR ( delay ) ;
}
ret = hv_debug_delay_files ( dev , delay ) ;
return ret ;
}
pr_debug ( " debugfs_hyperv: hyperv/ not in root debugfs path \n " ) ;
return PTR_ERR ( hv_debug_root ) ;
}
/* Remove dentry associated with released hv device */
void hv_debug_rm_dev_dir ( struct hv_device * dev )
{
if ( ! IS_ERR ( hv_debug_root ) )
debugfs_remove_recursive ( dev - > debug_dir ) ;
}
/* Remove all dentrys associated with vmbus testing */
void hv_debug_rm_all_dir ( void )
{
debugfs_remove_recursive ( hv_debug_root ) ;
}
/* Delay buffer/message reads on a vmbus channel */
void hv_debug_delay_test ( struct vmbus_channel * channel , enum delay delay_type )
{
struct vmbus_channel * test_channel = channel - > primary_channel ?
channel - > primary_channel :
channel ;
bool state = test_channel - > fuzz_testing_state ;
if ( state ) {
if ( delay_type = = 0 )
udelay ( test_channel - > fuzz_testing_interrupt_delay ) ;
else
udelay ( test_channel - > fuzz_testing_message_delay ) ;
}
}
/* Initialize top dentry for vmbus testing */
int hv_debug_init ( void )
{
hv_debug_root = debugfs_create_dir ( " hyperv " , NULL ) ;
if ( IS_ERR ( hv_debug_root ) ) {
pr_debug ( " debugfs_hyperv: hyperv/ not created \n " ) ;
return PTR_ERR ( hv_debug_root ) ;
}
return 0 ;
}