diff options
Diffstat (limited to 'fs/nfsd/fault_inject.c')
| -rw-r--r-- | fs/nfsd/fault_inject.c | 113 | 
1 files changed, 98 insertions, 15 deletions
diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c index e6c38159622..e761ee95617 100644 --- a/fs/nfsd/fault_inject.c +++ b/fs/nfsd/fault_inject.c @@ -8,61 +8,144 @@  #include <linux/fs.h>  #include <linux/debugfs.h>  #include <linux/module.h> +#include <linux/nsproxy.h> +#include <linux/sunrpc/clnt.h> +#include <asm/uaccess.h>  #include "state.h" -#include "fault_inject.h" +#include "netns.h"  struct nfsd_fault_inject_op {  	char *file; -	void (*func)(u64); +	u64 (*forget)(struct nfs4_client *, u64); +	u64 (*print)(struct nfs4_client *, u64);  };  static struct nfsd_fault_inject_op inject_ops[] = {  	{  		.file   = "forget_clients", -		.func   = nfsd_forget_clients, +		.forget = nfsd_forget_client, +		.print  = nfsd_print_client,  	},  	{  		.file   = "forget_locks", -		.func   = nfsd_forget_locks, +		.forget = nfsd_forget_client_locks, +		.print  = nfsd_print_client_locks,  	},  	{  		.file   = "forget_openowners", -		.func   = nfsd_forget_openowners, +		.forget = nfsd_forget_client_openowners, +		.print  = nfsd_print_client_openowners,  	},  	{  		.file   = "forget_delegations", -		.func   = nfsd_forget_delegations, +		.forget = nfsd_forget_client_delegations, +		.print  = nfsd_print_client_delegations,  	},  	{  		.file   = "recall_delegations", -		.func   = nfsd_recall_delegations, +		.forget = nfsd_recall_client_delegations, +		.print  = nfsd_print_client_delegations,  	},  };  static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op);  static struct dentry *debug_dir; -static int nfsd_inject_set(void *op_ptr, u64 val) +static void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val)  { -	struct nfsd_fault_inject_op *op = op_ptr; +	u64 count = 0;  	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); -	op->func(val); -	return 0; +	nfs4_lock_state(); +	count = nfsd_for_n_state(val, op->forget); +	nfs4_unlock_state(); +	printk(KERN_INFO "NFSD: %s: found %llu", op->file, count);  } -static int nfsd_inject_get(void *data, u64 *val) +static void nfsd_inject_set_client(struct nfsd_fault_inject_op *op, +				   struct sockaddr_storage *addr, +				   size_t addr_size)  { -	*val = 0; -	return 0; +	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); +		rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); +		printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file, buf, count); +	} +	nfs4_unlock_state(); +} + +static void nfsd_inject_get(struct nfsd_fault_inject_op *op, u64 *val) +{ +	nfs4_lock_state(); +	*val = nfsd_for_n_state(0, op->print); +	nfs4_unlock_state();  } -DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n"); +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) +		nfsd_inject_get(file->f_dentry->d_inode->i_private, &val); +	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) +{ +	char write_buf[INET6_ADDRSTRLEN]; +	size_t size = min(sizeof(write_buf) - 1, len); +	struct net *net = current->nsproxy->net_ns; +	struct sockaddr_storage sa; +	u64 val; + +	if (copy_from_user(write_buf, buf, size)) +		return -EFAULT; +	write_buf[size] = '\0'; + +	size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa)); +	if (size > 0) +		nfsd_inject_set_client(file->f_dentry->d_inode->i_private, &sa, size); +	else { +		val = simple_strtoll(write_buf, NULL, 0); +		nfsd_inject_set(file->f_dentry->d_inode->i_private, val); +	} +	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, +};  void nfsd_fault_inject_cleanup(void)  {  |