diff options
Diffstat (limited to 'fs/nfsd/fault_inject.c')
| -rw-r--r-- | fs/nfsd/fault_inject.c | 164 | 
1 files changed, 164 insertions, 0 deletions
diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c new file mode 100644 index 00000000000..2ed05c3cd43 --- /dev/null +++ b/fs/nfsd/fault_inject.c @@ -0,0 +1,164 @@ +/* + * 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> +#include <linux/nsproxy.h> +#include <linux/sunrpc/addr.h> +#include <asm/uaccess.h> + +#include "state.h" +#include "netns.h" + +struct nfsd_fault_inject_op { +	char *file; +	u64 (*forget)(struct nfs4_client *, u64); +	u64 (*print)(struct nfs4_client *, u64); +}; + +static struct nfsd_fault_inject_op inject_ops[] = { +	{ +		.file   = "forget_clients", +		.forget = nfsd_forget_client, +		.print  = nfsd_print_client, +	}, +	{ +		.file   = "forget_locks", +		.forget = nfsd_forget_client_locks, +		.print  = nfsd_print_client_locks, +	}, +	{ +		.file   = "forget_openowners", +		.forget = nfsd_forget_client_openowners, +		.print  = nfsd_print_client_openowners, +	}, +	{ +		.file   = "forget_delegations", +		.forget = nfsd_forget_client_delegations, +		.print  = nfsd_print_client_delegations, +	}, +	{ +		.file   = "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 void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val) +{ +	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); + +	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 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); +		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(); +} + +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; +	loff_t pos = *ppos; + +	if (!pos) +		nfsd_inject_get(file_inode(file)->i_private, &val); +	size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val); + +	return simple_read_from_buffer(buf, len, ppos, read_buf, size); +} + +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_inode(file)->i_private, &sa, size); +	else { +		val = simple_strtoll(write_buf, NULL, 0); +		nfsd_inject_set(file_inode(file)->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) +{ +	debugfs_remove_recursive(debug_dir); +} + +int nfsd_fault_inject_init(void) +{ +	unsigned int i; +	struct nfsd_fault_inject_op *op; +	umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; + +	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; +}  | 
