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 ;
2014-07-30 16:27:16 +04:00
u64 ( * get ) ( struct nfsd_fault_inject_op * ) ;
u64 ( * set_val ) ( struct nfsd_fault_inject_op * , u64 ) ;
u64 ( * set_clnt ) ( struct nfsd_fault_inject_op * ,
struct sockaddr_storage * , size_t ) ;
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 dentry * debug_dir ;
2014-07-30 16:27:16 +04:00
static u64 nfsd_inject_set ( struct nfsd_fault_inject_op * op , u64 val )
2011-11-01 21:35:21 +04:00
{
2014-07-30 16:27:16 +04:00
u64 count ;
2011-11-01 21:35:21 +04:00
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 ( ) ;
2014-07-30 16:27:16 +04:00
return count ;
2011-11-01 21:35:21 +04:00
}
2014-07-30 16:27:16 +04:00
static u64 nfsd_inject_set_client ( struct nfsd_fault_inject_op * op ,
2012-11-29 20:40:46 +04:00
struct sockaddr_storage * addr ,
size_t addr_size )
{
struct nfs4_client * clp ;
2014-07-30 16:27:16 +04:00
u64 count = 0 ;
2012-11-29 20:40:46 +04:00
nfs4_lock_state ( ) ;
clp = nfsd_find_client ( addr , addr_size ) ;
2014-07-30 16:27:16 +04:00
if ( clp )
2012-11-29 20:40:46 +04:00
count = op - > forget ( clp , 0 ) ;
nfs4_unlock_state ( ) ;
2014-07-30 16:27:16 +04:00
return count ;
2012-11-29 20:40:46 +04:00
}
2014-07-30 16:27:16 +04:00
static u64 nfsd_inject_get ( struct nfsd_fault_inject_op * op )
2011-11-01 21:35:21 +04:00
{
2014-07-30 16:27:16 +04:00
u64 count ;
2012-11-29 20:40:44 +04:00
nfs4_lock_state ( ) ;
2014-07-30 16:27:16 +04:00
count = nfsd_for_n_state ( 0 , op - > print ) ;
2012-11-29 20:40:44 +04:00
nfs4_unlock_state ( ) ;
2014-07-30 16:27:16 +04:00
return count ;
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 ] ;
2014-04-08 09:04:01 +04:00
size_t size ;
2012-11-29 20:40:45 +04:00
loff_t pos = * ppos ;
2014-07-30 16:27:16 +04:00
struct nfsd_fault_inject_op * op = file_inode ( file ) - > i_private ;
2012-11-29 20:40:45 +04:00
if ( ! pos )
2014-07-30 16:27:16 +04:00
val = op - > get ( op ) ;
2012-11-29 20:40:45 +04:00
size = scnprintf ( read_buf , sizeof ( read_buf ) , " %llu \n " , val ) ;
2014-04-08 09:04:01 +04:00
return simple_read_from_buffer ( buf , len , ppos , read_buf , size ) ;
2012-11-29 20:40:45 +04:00
}
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 ;
2014-07-30 16:27:16 +04:00
struct nfsd_fault_inject_op * op = file_inode ( file ) - > i_private ;
2012-11-29 20:40:45 +04:00
u64 val ;
2014-06-18 23:00:19 +04:00
char * nl ;
2012-11-29 20:40:45 +04:00
if ( copy_from_user ( write_buf , buf , size ) )
return - EFAULT ;
2012-11-29 20:40:46 +04:00
write_buf [ size ] = ' \0 ' ;
2014-06-18 23:00:19 +04:00
/* Deal with any embedded newlines in the string */
nl = strchr ( write_buf , ' \n ' ) ;
if ( nl ) {
size = nl - write_buf ;
* nl = ' \0 ' ;
}
2012-11-29 20:40:46 +04:00
size = rpc_pton ( net , write_buf , size , ( struct sockaddr * ) & sa , sizeof ( sa ) ) ;
2014-07-30 16:27:16 +04:00
if ( size > 0 ) {
val = op - > set_clnt ( op , & sa , size ) ;
if ( val )
pr_info ( " NFSD [%s]: Client %s had %llu state object(s) \n " ,
op - > file , write_buf , val ) ;
} else {
2012-11-29 20:40:46 +04:00
val = simple_strtoll ( write_buf , NULL , 0 ) ;
2014-07-30 16:27:16 +04:00
if ( val = = 0 )
pr_info ( " NFSD Fault Injection: %s (all) " , op - > file ) ;
else
pr_info ( " NFSD Fault Injection: %s (n = %llu) " ,
op - > file , val ) ;
val = op - > set_val ( op , val ) ;
pr_info ( " NFSD: %s: found %llu " , op - > file , 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 ) ;
}
2014-07-30 16:27:16 +04:00
static struct nfsd_fault_inject_op inject_ops [ ] = {
{
. file = " forget_clients " ,
2014-07-30 16:27:17 +04:00
. get = nfsd_inject_print_clients ,
2014-07-30 16:27:16 +04:00
. set_val = nfsd_inject_set ,
. set_clnt = nfsd_inject_set_client ,
. forget = nfsd_forget_client ,
} ,
{
. file = " forget_locks " ,
. get = nfsd_inject_get ,
. set_val = nfsd_inject_set ,
. set_clnt = nfsd_inject_set_client ,
. forget = nfsd_forget_client_locks ,
. print = nfsd_print_client_locks ,
} ,
{
. file = " forget_openowners " ,
. get = nfsd_inject_get ,
. set_val = nfsd_inject_set ,
. set_clnt = nfsd_inject_set_client ,
. forget = nfsd_forget_client_openowners ,
. print = nfsd_print_client_openowners ,
} ,
{
. file = " forget_delegations " ,
. get = nfsd_inject_get ,
. set_val = nfsd_inject_set ,
. set_clnt = nfsd_inject_set_client ,
. forget = nfsd_forget_client_delegations ,
. print = nfsd_print_client_delegations ,
} ,
{
. file = " recall_delegations " ,
. get = nfsd_inject_get ,
. set_val = nfsd_inject_set ,
. set_clnt = nfsd_inject_set_client ,
. forget = nfsd_recall_client_delegations ,
. print = nfsd_print_client_delegations ,
} ,
} ;
# define NUM_INJECT_OPS (sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op))
2011-11-01 21:35:21 +04:00
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 ;
}