aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2015-05-11 14:02:25 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2015-06-10 18:37:26 -0400
commit4a06825839889cc1756d0dd8a52d6b1071ee0263 (patch)
treeca49cf2fe5a77018dc473a261f8a5c243530c761
parent11598b8ff2b97cf034d0c025cf125c92b574bafc (diff)
SUNRPC: Transport fault injection
It has been exceptionally useful to exercise the logic that handles local immediate errors and RDMA connection loss. To enable developers to test this regularly and repeatably, add logic to simulate connection loss every so often. Fault injection is disabled by default. It is enabled with $ sudo echo xxx > /sys/kernel/debug/sunrpc/inject_fault/disconnect where "xxx" is a large positive number of transport method calls before a disconnect. A value of several thousand is usually a good number that allows reasonable forward progress while still causing a lot of connection drops. These hooks are disabled when SUNRPC_DEBUG is turned off. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r--include/linux/sunrpc/xprt.h19
-rw-r--r--net/sunrpc/clnt.c1
-rw-r--r--net/sunrpc/debugfs.c77
-rw-r--r--net/sunrpc/xprt.c2
-rw-r--r--net/sunrpc/xprtrdma/transport.c11
-rw-r--r--net/sunrpc/xprtsock.c10
6 files changed, 120 insertions, 0 deletions
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index b6096592b1d4..0fb9acbb4780 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -135,6 +135,7 @@ struct rpc_xprt_ops {
135 void (*print_stats)(struct rpc_xprt *xprt, struct seq_file *seq); 135 void (*print_stats)(struct rpc_xprt *xprt, struct seq_file *seq);
136 int (*enable_swap)(struct rpc_xprt *xprt); 136 int (*enable_swap)(struct rpc_xprt *xprt);
137 void (*disable_swap)(struct rpc_xprt *xprt); 137 void (*disable_swap)(struct rpc_xprt *xprt);
138 void (*inject_disconnect)(struct rpc_xprt *xprt);
138}; 139};
139 140
140/* 141/*
@@ -244,6 +245,7 @@ struct rpc_xprt {
244 const char *address_strings[RPC_DISPLAY_MAX]; 245 const char *address_strings[RPC_DISPLAY_MAX];
245#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 246#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
246 struct dentry *debugfs; /* debugfs directory */ 247 struct dentry *debugfs; /* debugfs directory */
248 atomic_t inject_disconnect;
247#endif 249#endif
248}; 250};
249 251
@@ -445,6 +447,23 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt)
445 return test_and_set_bit(XPRT_BINDING, &xprt->state); 447 return test_and_set_bit(XPRT_BINDING, &xprt->state);
446} 448}
447 449
450#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
451extern unsigned int rpc_inject_disconnect;
452static inline void xprt_inject_disconnect(struct rpc_xprt *xprt)
453{
454 if (!rpc_inject_disconnect)
455 return;
456 if (atomic_dec_return(&xprt->inject_disconnect))
457 return;
458 atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
459 xprt->ops->inject_disconnect(xprt);
460}
461#else
462static inline void xprt_inject_disconnect(struct rpc_xprt *xprt)
463{
464}
465#endif
466
448#endif /* __KERNEL__*/ 467#endif /* __KERNEL__*/
449 468
450#endif /* _LINUX_SUNRPC_XPRT_H */ 469#endif /* _LINUX_SUNRPC_XPRT_H */
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 576d6ae39f25..f41ed882ed3c 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -1605,6 +1605,7 @@ call_allocate(struct rpc_task *task)
1605 req->rq_callsize + req->rq_rcvsize); 1605 req->rq_callsize + req->rq_rcvsize);
1606 if (req->rq_buffer != NULL) 1606 if (req->rq_buffer != NULL)
1607 return; 1607 return;
1608 xprt_inject_disconnect(xprt);
1608 1609
1609 dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid); 1610 dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid);
1610 1611
diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c
index 82962f7e6e88..7cc1b8a6ef6d 100644
--- a/net/sunrpc/debugfs.c
+++ b/net/sunrpc/debugfs.c
@@ -10,9 +10,12 @@
10#include "netns.h" 10#include "netns.h"
11 11
12static struct dentry *topdir; 12static struct dentry *topdir;
13static struct dentry *rpc_fault_dir;
13static struct dentry *rpc_clnt_dir; 14static struct dentry *rpc_clnt_dir;
14static struct dentry *rpc_xprt_dir; 15static struct dentry *rpc_xprt_dir;
15 16
17unsigned int rpc_inject_disconnect;
18
16struct rpc_clnt_iter { 19struct rpc_clnt_iter {
17 struct rpc_clnt *clnt; 20 struct rpc_clnt *clnt;
18 loff_t pos; 21 loff_t pos;
@@ -257,6 +260,8 @@ rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
257 debugfs_remove_recursive(xprt->debugfs); 260 debugfs_remove_recursive(xprt->debugfs);
258 xprt->debugfs = NULL; 261 xprt->debugfs = NULL;
259 } 262 }
263
264 atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
260} 265}
261 266
262void 267void
@@ -266,11 +271,78 @@ rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
266 xprt->debugfs = NULL; 271 xprt->debugfs = NULL;
267} 272}
268 273
274static int
275fault_open(struct inode *inode, struct file *filp)
276{
277 filp->private_data = kmalloc(128, GFP_KERNEL);
278 if (!filp->private_data)
279 return -ENOMEM;
280 return 0;
281}
282
283static int
284fault_release(struct inode *inode, struct file *filp)
285{
286 kfree(filp->private_data);
287 return 0;
288}
289
290static ssize_t
291fault_disconnect_read(struct file *filp, char __user *user_buf,
292 size_t len, loff_t *offset)
293{
294 char *buffer = (char *)filp->private_data;
295 size_t size;
296
297 size = sprintf(buffer, "%u\n", rpc_inject_disconnect);
298 return simple_read_from_buffer(user_buf, len, offset, buffer, size);
299}
300
301static ssize_t
302fault_disconnect_write(struct file *filp, const char __user *user_buf,
303 size_t len, loff_t *offset)
304{
305 char buffer[16];
306
307 len = min(len, sizeof(buffer) - 1);
308 if (copy_from_user(buffer, user_buf, len))
309 return -EFAULT;
310 buffer[len] = '\0';
311 if (kstrtouint(buffer, 10, &rpc_inject_disconnect))
312 return -EINVAL;
313 return len;
314}
315
316static const struct file_operations fault_disconnect_fops = {
317 .owner = THIS_MODULE,
318 .open = fault_open,
319 .read = fault_disconnect_read,
320 .write = fault_disconnect_write,
321 .release = fault_release,
322};
323
324static struct dentry *
325inject_fault_dir(struct dentry *topdir)
326{
327 struct dentry *faultdir;
328
329 faultdir = debugfs_create_dir("inject_fault", topdir);
330 if (!faultdir)
331 return NULL;
332
333 if (!debugfs_create_file("disconnect", S_IFREG | S_IRUSR, faultdir,
334 NULL, &fault_disconnect_fops))
335 return NULL;
336
337 return faultdir;
338}
339
269void __exit 340void __exit
270sunrpc_debugfs_exit(void) 341sunrpc_debugfs_exit(void)
271{ 342{
272 debugfs_remove_recursive(topdir); 343 debugfs_remove_recursive(topdir);
273 topdir = NULL; 344 topdir = NULL;
345 rpc_fault_dir = NULL;
274 rpc_clnt_dir = NULL; 346 rpc_clnt_dir = NULL;
275 rpc_xprt_dir = NULL; 347 rpc_xprt_dir = NULL;
276} 348}
@@ -282,6 +354,10 @@ sunrpc_debugfs_init(void)
282 if (!topdir) 354 if (!topdir)
283 return; 355 return;
284 356
357 rpc_fault_dir = inject_fault_dir(topdir);
358 if (!rpc_fault_dir)
359 goto out_remove;
360
285 rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); 361 rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
286 if (!rpc_clnt_dir) 362 if (!rpc_clnt_dir)
287 goto out_remove; 363 goto out_remove;
@@ -294,5 +370,6 @@ sunrpc_debugfs_init(void)
294out_remove: 370out_remove:
295 debugfs_remove_recursive(topdir); 371 debugfs_remove_recursive(topdir);
296 topdir = NULL; 372 topdir = NULL;
373 rpc_fault_dir = NULL;
297 rpc_clnt_dir = NULL; 374 rpc_clnt_dir = NULL;
298} 375}
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 1d4fe24af06a..e1fb538e10e0 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -967,6 +967,7 @@ void xprt_transmit(struct rpc_task *task)
967 task->tk_status = status; 967 task->tk_status = status;
968 return; 968 return;
969 } 969 }
970 xprt_inject_disconnect(xprt);
970 971
971 dprintk("RPC: %5u xmit complete\n", task->tk_pid); 972 dprintk("RPC: %5u xmit complete\n", task->tk_pid);
972 task->tk_flags |= RPC_TASK_SENT; 973 task->tk_flags |= RPC_TASK_SENT;
@@ -1285,6 +1286,7 @@ void xprt_release(struct rpc_task *task)
1285 spin_unlock_bh(&xprt->transport_lock); 1286 spin_unlock_bh(&xprt->transport_lock);
1286 if (req->rq_buffer) 1287 if (req->rq_buffer)
1287 xprt->ops->buf_free(req->rq_buffer); 1288 xprt->ops->buf_free(req->rq_buffer);
1289 xprt_inject_disconnect(xprt);
1288 if (req->rq_cred != NULL) 1290 if (req->rq_cred != NULL)
1289 put_rpccred(req->rq_cred); 1291 put_rpccred(req->rq_cred);
1290 task->tk_rqstp = NULL; 1292 task->tk_rqstp = NULL;
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index ebf6fe759f0e..b8aac23b1b0c 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -246,6 +246,16 @@ xprt_rdma_connect_worker(struct work_struct *work)
246 xprt_clear_connecting(xprt); 246 xprt_clear_connecting(xprt);
247} 247}
248 248
249static void
250xprt_rdma_inject_disconnect(struct rpc_xprt *xprt)
251{
252 struct rpcrdma_xprt *r_xprt = container_of(xprt, struct rpcrdma_xprt,
253 rx_xprt);
254
255 pr_info("rpcrdma: injecting transport disconnect on xprt=%p\n", xprt);
256 rdma_disconnect(r_xprt->rx_ia.ri_id);
257}
258
249/* 259/*
250 * xprt_rdma_destroy 260 * xprt_rdma_destroy
251 * 261 *
@@ -714,6 +724,7 @@ static struct rpc_xprt_ops xprt_rdma_procs = {
714 .print_stats = xprt_rdma_print_stats, 724 .print_stats = xprt_rdma_print_stats,
715 .enable_swap = xprt_rdma_enable_swap, 725 .enable_swap = xprt_rdma_enable_swap,
716 .disable_swap = xprt_rdma_disable_swap, 726 .disable_swap = xprt_rdma_disable_swap,
727 .inject_disconnect = xprt_rdma_inject_disconnect
717}; 728};
718 729
719static struct xprt_class xprt_rdma = { 730static struct xprt_class xprt_rdma = {
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index bf20726d4ab5..fda8ec8c74c0 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -866,6 +866,13 @@ static void xs_close(struct rpc_xprt *xprt)
866 xprt_disconnect_done(xprt); 866 xprt_disconnect_done(xprt);
867} 867}
868 868
869static void xs_inject_disconnect(struct rpc_xprt *xprt)
870{
871 dprintk("RPC: injecting transport disconnect on xprt=%p\n",
872 xprt);
873 xprt_disconnect_done(xprt);
874}
875
869static void xs_xprt_free(struct rpc_xprt *xprt) 876static void xs_xprt_free(struct rpc_xprt *xprt)
870{ 877{
871 xs_free_peer_addresses(xprt); 878 xs_free_peer_addresses(xprt);
@@ -2522,6 +2529,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
2522 .print_stats = xs_udp_print_stats, 2529 .print_stats = xs_udp_print_stats,
2523 .enable_swap = xs_enable_swap, 2530 .enable_swap = xs_enable_swap,
2524 .disable_swap = xs_disable_swap, 2531 .disable_swap = xs_disable_swap,
2532 .inject_disconnect = xs_inject_disconnect,
2525}; 2533};
2526 2534
2527static struct rpc_xprt_ops xs_tcp_ops = { 2535static struct rpc_xprt_ops xs_tcp_ops = {
@@ -2540,6 +2548,7 @@ static struct rpc_xprt_ops xs_tcp_ops = {
2540 .print_stats = xs_tcp_print_stats, 2548 .print_stats = xs_tcp_print_stats,
2541 .enable_swap = xs_enable_swap, 2549 .enable_swap = xs_enable_swap,
2542 .disable_swap = xs_disable_swap, 2550 .disable_swap = xs_disable_swap,
2551 .inject_disconnect = xs_inject_disconnect,
2543}; 2552};
2544 2553
2545/* 2554/*
@@ -2559,6 +2568,7 @@ static struct rpc_xprt_ops bc_tcp_ops = {
2559 .print_stats = xs_tcp_print_stats, 2568 .print_stats = xs_tcp_print_stats,
2560 .enable_swap = xs_enable_swap, 2569 .enable_swap = xs_enable_swap,
2561 .disable_swap = xs_disable_swap, 2570 .disable_swap = xs_disable_swap,
2571 .inject_disconnect = xs_inject_disconnect,
2562}; 2572};
2563 2573
2564static int xs_init_anyaddr(const int family, struct sockaddr *sap) 2574static int xs_init_anyaddr(const int family, struct sockaddr *sap)