1/* 2 * Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> 3 * 4 * Uses debugfs to create fault injection points for client testing 5 */ 6 7#include <linux/types.h> 8#include <linux/fs.h> 9#include <linux/debugfs.h> 10#include <linux/module.h> 11#include <linux/nsproxy.h> 12#include <linux/sunrpc/addr.h> 13#include <asm/uaccess.h> 14 15#include "state.h" 16#include "netns.h" 17 18struct nfsd_fault_inject_op { 19 char *file; 20 u64 (*get)(void); 21 u64 (*set_val)(u64); 22 u64 (*set_clnt)(struct sockaddr_storage *, size_t); 23}; 24 25static struct dentry *debug_dir; 26 27static ssize_t fault_inject_read(struct file *file, char __user *buf, 28 size_t len, loff_t *ppos) 29{ 30 static u64 val; 31 char read_buf[25]; 32 size_t size; 33 loff_t pos = *ppos; 34 struct nfsd_fault_inject_op *op = file_inode(file)->i_private; 35 36 if (!pos) 37 val = op->get(); 38 size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val); 39 40 return simple_read_from_buffer(buf, len, ppos, read_buf, size); 41} 42 43static ssize_t fault_inject_write(struct file *file, const char __user *buf, 44 size_t len, loff_t *ppos) 45{ 46 char write_buf[INET6_ADDRSTRLEN]; 47 size_t size = min(sizeof(write_buf) - 1, len); 48 struct net *net = current->nsproxy->net_ns; 49 struct sockaddr_storage sa; 50 struct nfsd_fault_inject_op *op = file_inode(file)->i_private; 51 u64 val; 52 char *nl; 53 54 if (copy_from_user(write_buf, buf, size)) 55 return -EFAULT; 56 write_buf[size] = '\0'; 57 58 /* Deal with any embedded newlines in the string */ 59 nl = strchr(write_buf, '\n'); 60 if (nl) { 61 size = nl - write_buf; 62 *nl = '\0'; 63 } 64 65 size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa)); 66 if (size > 0) { 67 val = op->set_clnt(&sa, size); 68 if (val) 69 pr_info("NFSD [%s]: Client %s had %llu state object(s)\n", 70 op->file, write_buf, val); 71 } else { 72 val = simple_strtoll(write_buf, NULL, 0); 73 if (val == 0) 74 pr_info("NFSD Fault Injection: %s (all)", op->file); 75 else 76 pr_info("NFSD Fault Injection: %s (n = %llu)", 77 op->file, val); 78 val = op->set_val(val); 79 pr_info("NFSD: %s: found %llu", op->file, val); 80 } 81 return len; /* on success, claim we got the whole input */ 82} 83 84static const struct file_operations fops_nfsd = { 85 .owner = THIS_MODULE, 86 .read = fault_inject_read, 87 .write = fault_inject_write, 88}; 89 90void nfsd_fault_inject_cleanup(void) 91{ 92 debugfs_remove_recursive(debug_dir); 93} 94 95static struct nfsd_fault_inject_op inject_ops[] = { 96 { 97 .file = "forget_clients", 98 .get = nfsd_inject_print_clients, 99 .set_val = nfsd_inject_forget_clients, 100 .set_clnt = nfsd_inject_forget_client, 101 }, 102 { 103 .file = "forget_locks", 104 .get = nfsd_inject_print_locks, 105 .set_val = nfsd_inject_forget_locks, 106 .set_clnt = nfsd_inject_forget_client_locks, 107 }, 108 { 109 .file = "forget_openowners", 110 .get = nfsd_inject_print_openowners, 111 .set_val = nfsd_inject_forget_openowners, 112 .set_clnt = nfsd_inject_forget_client_openowners, 113 }, 114 { 115 .file = "forget_delegations", 116 .get = nfsd_inject_print_delegations, 117 .set_val = nfsd_inject_forget_delegations, 118 .set_clnt = nfsd_inject_forget_client_delegations, 119 }, 120 { 121 .file = "recall_delegations", 122 .get = nfsd_inject_print_delegations, 123 .set_val = nfsd_inject_recall_delegations, 124 .set_clnt = nfsd_inject_recall_client_delegations, 125 }, 126}; 127 128#define NUM_INJECT_OPS (sizeof(inject_ops)/sizeof(struct nfsd_fault_inject_op)) 129 130int nfsd_fault_inject_init(void) 131{ 132 unsigned int i; 133 struct nfsd_fault_inject_op *op; 134 umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; 135 136 debug_dir = debugfs_create_dir("nfsd", NULL); 137 if (!debug_dir) 138 goto fail; 139 140 for (i = 0; i < NUM_INJECT_OPS; i++) { 141 op = &inject_ops[i]; 142 if (!debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd)) 143 goto fail; 144 } 145 return 0; 146 147fail: 148 nfsd_fault_inject_cleanup(); 149 return -ENOMEM; 150} 151