2011-11-01 21:35:21 +04:00
/*
* Copyright ( c ) 2011 Bryan Schumaker < bjschuma @ netapp . com >
*
* Uses debugfs to create fault injection points for client testing
*/
# include <linux/types.h>
# include <linux/fs.h>
# include <linux/debugfs.h>
# include <linux/module.h>
2012-11-29 20:40:46 +04:00
# include <linux/nsproxy.h>
2013-02-04 21:50:00 +04:00
# include <linux/sunrpc/addr.h>
2012-11-29 20:40:45 +04:00
# include <asm/uaccess.h>
2011-11-01 21:35:21 +04:00
# include "state.h"
2012-11-29 20:40:46 +04:00
# include "netns.h"
2011-11-01 21:35:21 +04:00
struct nfsd_fault_inject_op {
char * file ;
2012-11-29 20:40:43 +04:00
u64 ( * forget ) ( struct nfs4_client * , u64 ) ;
2012-11-29 20:40:44 +04:00
u64 ( * print ) ( struct nfs4_client * , u64 ) ;
2011-11-01 21:35:21 +04:00
} ;
static struct nfsd_fault_inject_op inject_ops [ ] = {
{
. file = " forget_clients " ,
2012-11-29 20:40:43 +04:00
. forget = nfsd_forget_client ,
2012-11-29 20:40:44 +04:00
. print = nfsd_print_client ,
2011-11-01 21:35:21 +04:00
} ,
{
. file = " forget_locks " ,
2012-11-29 20:40:43 +04:00
. forget = nfsd_forget_client_locks ,
2012-11-29 20:40:44 +04:00
. print = nfsd_print_client_locks ,
2011-11-01 21:35:21 +04:00
} ,
{
. file = " forget_openowners " ,
2012-11-29 20:40:43 +04:00
. forget = nfsd_forget_client_openowners ,
2012-11-29 20:40:44 +04:00
. print = nfsd_print_client_openowners ,
2011-11-01 21:35:21 +04:00
} ,
{
. file = " forget_delegations " ,
2012-11-29 20:40:43 +04:00
. forget = nfsd_forget_client_delegations ,
2012-11-29 20:40:44 +04:00
. print = nfsd_print_client_delegations ,
2011-11-01 21:35:21 +04:00
} ,
{
. file = " recall_delegations " ,
2012-11-29 20:40:43 +04:00
. forget = nfsd_recall_client_delegations ,
2012-11-29 20:40:44 +04:00
. print = nfsd_print_client_delegations ,
2011-11-01 21:35:21 +04:00
} ,
} ;
static long int NUM_INJECT_OPS = sizeof ( inject_ops ) / sizeof ( struct nfsd_fault_inject_op ) ;
static struct dentry * debug_dir ;
2012-11-29 20:40:45 +04:00
static void nfsd_inject_set ( struct nfsd_fault_inject_op * op , u64 val )
2011-11-01 21:35:21 +04:00
{
2012-11-29 20:40:43 +04:00
u64 count = 0 ;
2011-11-01 21:35:21 +04:00
if ( val = = 0 )
printk ( KERN_INFO " NFSD Fault Injection: %s (all) " , op - > file ) ;
else
printk ( KERN_INFO " NFSD Fault Injection: %s (n = %llu) " , op - > file , val ) ;
2012-11-29 20:40:38 +04:00
nfs4_lock_state ( ) ;
2012-11-29 20:40:43 +04:00
count = nfsd_for_n_state ( val , op - > forget ) ;
2012-11-29 20:40:38 +04:00
nfs4_unlock_state ( ) ;
2012-11-29 20:40:43 +04:00
printk ( KERN_INFO " NFSD: %s: found %llu " , op - > file , count ) ;
2011-11-01 21:35:21 +04:00
}
2012-11-29 20:40:46 +04:00
static void nfsd_inject_set_client ( struct nfsd_fault_inject_op * op ,
struct sockaddr_storage * addr ,
size_t addr_size )
{
char buf [ INET6_ADDRSTRLEN ] ;
struct nfs4_client * clp ;
u64 count ;
nfs4_lock_state ( ) ;
clp = nfsd_find_client ( addr , addr_size ) ;
if ( clp ) {
count = op - > forget ( clp , 0 ) ;
2012-12-08 01:17:28 +04:00
rpc_ntop ( ( struct sockaddr * ) & clp - > cl_addr , buf , sizeof ( buf ) ) ;
2012-11-29 20:40:46 +04:00
printk ( KERN_INFO " NFSD [%s]: Client %s had %llu state object(s) \n " , op - > file , buf , count ) ;
}
nfs4_unlock_state ( ) ;
}
2012-11-29 20:40:45 +04:00
static void nfsd_inject_get ( struct nfsd_fault_inject_op * op , u64 * val )
2011-11-01 21:35:21 +04:00
{
2012-11-29 20:40:44 +04:00
nfs4_lock_state ( ) ;
* val = nfsd_for_n_state ( 0 , op - > print ) ;
nfs4_unlock_state ( ) ;
2011-11-01 21:35:21 +04:00
}
2012-11-29 20:40:45 +04:00
static ssize_t fault_inject_read ( struct file * file , char __user * buf ,
size_t len , loff_t * ppos )
{
static u64 val ;
char read_buf [ 25 ] ;
size_t size , ret ;
loff_t pos = * ppos ;
if ( ! pos )
2013-01-24 02:07:38 +04:00
nfsd_inject_get ( file_inode ( file ) - > i_private , & val ) ;
2012-11-29 20:40:45 +04:00
size = scnprintf ( read_buf , sizeof ( read_buf ) , " %llu \n " , val ) ;
if ( pos < 0 )
return - EINVAL ;
if ( pos > = size | | ! len )
return 0 ;
if ( len > size - pos )
len = size - pos ;
ret = copy_to_user ( buf , read_buf + pos , len ) ;
if ( ret = = len )
return - EFAULT ;
len - = ret ;
* ppos = pos + len ;
return len ;
}
static ssize_t fault_inject_write ( struct file * file , const char __user * buf ,
size_t len , loff_t * ppos )
{
2012-11-29 20:40:46 +04:00
char write_buf [ INET6_ADDRSTRLEN ] ;
2012-12-08 01:17:29 +04:00
size_t size = min ( sizeof ( write_buf ) - 1 , len ) ;
2012-11-29 20:40:46 +04:00
struct net * net = current - > nsproxy - > net_ns ;
struct sockaddr_storage sa ;
2012-11-29 20:40:45 +04:00
u64 val ;
if ( copy_from_user ( write_buf , buf , size ) )
return - EFAULT ;
2012-11-29 20:40:46 +04:00
write_buf [ size ] = ' \0 ' ;
size = rpc_pton ( net , write_buf , size , ( struct sockaddr * ) & sa , sizeof ( sa ) ) ;
if ( size > 0 )
2013-01-24 02:07:38 +04:00
nfsd_inject_set_client ( file_inode ( file ) - > i_private , & sa , size ) ;
2012-11-29 20:40:46 +04:00
else {
val = simple_strtoll ( write_buf , NULL , 0 ) ;
2013-01-24 02:07:38 +04:00
nfsd_inject_set ( file_inode ( file ) - > i_private , val ) ;
2012-11-29 20:40:46 +04:00
}
2012-11-29 20:40:45 +04:00
return len ; /* on success, claim we got the whole input */
}
static const struct file_operations fops_nfsd = {
. owner = THIS_MODULE ,
. read = fault_inject_read ,
. write = fault_inject_write ,
} ;
2011-11-01 21:35:21 +04:00
void nfsd_fault_inject_cleanup ( void )
{
debugfs_remove_recursive ( debug_dir ) ;
}
int nfsd_fault_inject_init ( void )
{
unsigned int i ;
struct nfsd_fault_inject_op * op ;
2012-03-20 14:00:24 +04:00
umode_t mode = S_IFREG | S_IRUSR | S_IWUSR ;
2011-11-01 21:35:21 +04:00
debug_dir = debugfs_create_dir ( " nfsd " , NULL ) ;
if ( ! debug_dir )
goto fail ;
for ( i = 0 ; i < NUM_INJECT_OPS ; i + + ) {
op = & inject_ops [ i ] ;
if ( ! debugfs_create_file ( op - > file , mode , debug_dir , op , & fops_nfsd ) )
goto fail ;
}
return 0 ;
fail :
nfsd_fault_inject_cleanup ( ) ;
return - ENOMEM ;
}