Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SUNRPC: Move client-side disconnect injection

Disconnect injection stress-tests the ability for both client and
server implementations to behave resiliently in the face of network
instability.

Convert the existing client-side disconnect injection infrastructure
to use the kernel's generic error injection facility. The generic
facility has a richer set of injection criteria.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

+32 -84
-18
include/linux/sunrpc/xprt.h
··· 288 288 const char *address_strings[RPC_DISPLAY_MAX]; 289 289 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 290 290 struct dentry *debugfs; /* debugfs directory */ 291 - atomic_t inject_disconnect; 292 291 #endif 293 292 struct rcu_head rcu; 294 293 const struct xprt_class *xprt_class; ··· 500 501 { 501 502 return test_and_set_bit(XPRT_BINDING, &xprt->state); 502 503 } 503 - 504 - #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 505 - extern unsigned int rpc_inject_disconnect; 506 - static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) 507 - { 508 - if (!rpc_inject_disconnect) 509 - return; 510 - if (atomic_dec_return(&xprt->inject_disconnect)) 511 - return; 512 - atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect); 513 - xprt->ops->inject_disconnect(xprt); 514 - } 515 - #else 516 - static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) 517 - { 518 - } 519 - #endif 520 504 521 505 #endif /* _LINUX_SUNRPC_XPRT_H */
+16 -66
net/sunrpc/debugfs.c
··· 16 16 static struct dentry *rpc_clnt_dir; 17 17 static struct dentry *rpc_xprt_dir; 18 18 19 - unsigned int rpc_inject_disconnect; 20 - 21 19 static int 22 20 tasks_show(struct seq_file *f, void *v) 23 21 { ··· 235 237 /* make tasks file */ 236 238 debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt, 237 239 &xprt_info_fops); 238 - 239 - atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect); 240 240 } 241 241 242 242 void ··· 244 248 xprt->debugfs = NULL; 245 249 } 246 250 247 - static int 248 - fault_open(struct inode *inode, struct file *filp) 249 - { 250 - filp->private_data = kmalloc(128, GFP_KERNEL); 251 - if (!filp->private_data) 252 - return -ENOMEM; 253 - return 0; 254 - } 255 - 256 - static int 257 - fault_release(struct inode *inode, struct file *filp) 258 - { 259 - kfree(filp->private_data); 260 - return 0; 261 - } 262 - 263 - static ssize_t 264 - fault_disconnect_read(struct file *filp, char __user *user_buf, 265 - size_t len, loff_t *offset) 266 - { 267 - char *buffer = (char *)filp->private_data; 268 - size_t size; 269 - 270 - size = sprintf(buffer, "%u\n", rpc_inject_disconnect); 271 - return simple_read_from_buffer(user_buf, len, offset, buffer, size); 272 - } 273 - 274 - static ssize_t 275 - fault_disconnect_write(struct file *filp, const char __user *user_buf, 276 - size_t len, loff_t *offset) 277 - { 278 - char buffer[16]; 279 - 280 - if (len >= sizeof(buffer)) 281 - len = sizeof(buffer) - 1; 282 - if (copy_from_user(buffer, user_buf, len)) 283 - return -EFAULT; 284 - buffer[len] = '\0'; 285 - if (kstrtouint(buffer, 10, &rpc_inject_disconnect)) 286 - return -EINVAL; 287 - return len; 288 - } 289 - 290 - static const struct file_operations fault_disconnect_fops = { 291 - .owner = THIS_MODULE, 292 - .open = fault_open, 293 - .read = fault_disconnect_read, 294 - .write = fault_disconnect_write, 295 - .release = fault_release, 296 - }; 297 - 298 251 #if IS_ENABLED(CONFIG_FAIL_SUNRPC) 299 252 struct fail_sunrpc_attr fail_sunrpc = { 300 253 .attr = FAULT_ATTR_INITIALIZER, 301 254 }; 302 255 EXPORT_SYMBOL_GPL(fail_sunrpc); 256 + 257 + static void fail_sunrpc_init(void) 258 + { 259 + struct dentry *dir; 260 + 261 + dir = fault_create_debugfs_attr("fail_sunrpc", NULL, 262 + &fail_sunrpc.attr); 263 + 264 + debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir, 265 + &fail_sunrpc.ignore_client_disconnect); 266 + } 267 + #else 268 + static void fail_sunrpc_init(void) 269 + { 270 + } 303 271 #endif 304 272 305 273 void __exit ··· 278 318 void __init 279 319 sunrpc_debugfs_init(void) 280 320 { 281 - struct dentry *rpc_fault_dir; 282 - 283 321 topdir = debugfs_create_dir("sunrpc", NULL); 284 322 285 323 rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); 286 324 287 325 rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); 288 326 289 - rpc_fault_dir = debugfs_create_dir("inject_fault", topdir); 290 - 291 - debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL, 292 - &fault_disconnect_fops); 293 - 294 - #if IS_ENABLED(CONFIG_FAIL_SUNRPC) 295 - fault_create_debugfs_attr("fail_sunrpc", NULL, 296 - &fail_sunrpc.attr); 297 - #endif 327 + fail_sunrpc_init(); 298 328 }
+2
net/sunrpc/fail.h
··· 12 12 13 13 struct fail_sunrpc_attr { 14 14 struct fault_attr attr; 15 + 16 + bool ignore_client_disconnect; 15 17 }; 16 18 17 19 extern struct fail_sunrpc_attr fail_sunrpc;
+14
net/sunrpc/xprt.c
··· 56 56 57 57 #include "sunrpc.h" 58 58 #include "sysfs.h" 59 + #include "fail.h" 59 60 60 61 /* 61 62 * Local variables ··· 855 854 return; 856 855 queue_work(xprtiod_workqueue, &xprt->task_cleanup); 857 856 } 857 + 858 + #if IS_ENABLED(CONFIG_FAIL_SUNRPC) 859 + static void xprt_inject_disconnect(struct rpc_xprt *xprt) 860 + { 861 + if (!fail_sunrpc.ignore_client_disconnect && 862 + should_fail(&fail_sunrpc.attr, 1)) 863 + xprt->ops->inject_disconnect(xprt); 864 + } 865 + #else 866 + static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) 867 + { 868 + } 869 + #endif 858 870 859 871 bool xprt_lock_connect(struct rpc_xprt *xprt, 860 872 struct rpc_task *task,