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