Commit a4ae3081 authored by Chuck Lever's avatar Chuck Lever

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: default avatarChuck Lever <chuck.lever@oracle.com>
parent c782af25
...@@ -288,7 +288,6 @@ struct rpc_xprt { ...@@ -288,7 +288,6 @@ struct rpc_xprt {
const char *address_strings[RPC_DISPLAY_MAX]; const char *address_strings[RPC_DISPLAY_MAX];
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
struct dentry *debugfs; /* debugfs directory */ struct dentry *debugfs; /* debugfs directory */
atomic_t inject_disconnect;
#endif #endif
struct rcu_head rcu; struct rcu_head rcu;
const struct xprt_class *xprt_class; const struct xprt_class *xprt_class;
...@@ -502,21 +501,4 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt) ...@@ -502,21 +501,4 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt)
return test_and_set_bit(XPRT_BINDING, &xprt->state); return test_and_set_bit(XPRT_BINDING, &xprt->state);
} }
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
extern unsigned int rpc_inject_disconnect;
static inline void xprt_inject_disconnect(struct rpc_xprt *xprt)
{
if (!rpc_inject_disconnect)
return;
if (atomic_dec_return(&xprt->inject_disconnect))
return;
atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
xprt->ops->inject_disconnect(xprt);
}
#else
static inline void xprt_inject_disconnect(struct rpc_xprt *xprt)
{
}
#endif
#endif /* _LINUX_SUNRPC_XPRT_H */ #endif /* _LINUX_SUNRPC_XPRT_H */
...@@ -16,8 +16,6 @@ static struct dentry *topdir; ...@@ -16,8 +16,6 @@ static struct dentry *topdir;
static struct dentry *rpc_clnt_dir; static struct dentry *rpc_clnt_dir;
static struct dentry *rpc_xprt_dir; static struct dentry *rpc_xprt_dir;
unsigned int rpc_inject_disconnect;
static int static int
tasks_show(struct seq_file *f, void *v) tasks_show(struct seq_file *f, void *v)
{ {
...@@ -237,8 +235,6 @@ rpc_xprt_debugfs_register(struct rpc_xprt *xprt) ...@@ -237,8 +235,6 @@ rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
/* make tasks file */ /* make tasks file */
debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt, debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt,
&xprt_info_fops); &xprt_info_fops);
atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
} }
void void
...@@ -248,62 +244,26 @@ rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt) ...@@ -248,62 +244,26 @@ rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
xprt->debugfs = NULL; xprt->debugfs = NULL;
} }
static int #if IS_ENABLED(CONFIG_FAIL_SUNRPC)
fault_open(struct inode *inode, struct file *filp) struct fail_sunrpc_attr fail_sunrpc = {
{ .attr = FAULT_ATTR_INITIALIZER,
filp->private_data = kmalloc(128, GFP_KERNEL); };
if (!filp->private_data) EXPORT_SYMBOL_GPL(fail_sunrpc);
return -ENOMEM;
return 0;
}
static int static void fail_sunrpc_init(void)
fault_release(struct inode *inode, struct file *filp)
{ {
kfree(filp->private_data); struct dentry *dir;
return 0;
}
static ssize_t dir = fault_create_debugfs_attr("fail_sunrpc", NULL,
fault_disconnect_read(struct file *filp, char __user *user_buf, &fail_sunrpc.attr);
size_t len, loff_t *offset)
{
char *buffer = (char *)filp->private_data;
size_t size;
size = sprintf(buffer, "%u\n", rpc_inject_disconnect); debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir,
return simple_read_from_buffer(user_buf, len, offset, buffer, size); &fail_sunrpc.ignore_client_disconnect);
} }
#else
static ssize_t static void fail_sunrpc_init(void)
fault_disconnect_write(struct file *filp, const char __user *user_buf,
size_t len, loff_t *offset)
{ {
char buffer[16];
if (len >= sizeof(buffer))
len = sizeof(buffer) - 1;
if (copy_from_user(buffer, user_buf, len))
return -EFAULT;
buffer[len] = '\0';
if (kstrtouint(buffer, 10, &rpc_inject_disconnect))
return -EINVAL;
return len;
} }
static const struct file_operations fault_disconnect_fops = {
.owner = THIS_MODULE,
.open = fault_open,
.read = fault_disconnect_read,
.write = fault_disconnect_write,
.release = fault_release,
};
#if IS_ENABLED(CONFIG_FAIL_SUNRPC)
struct fail_sunrpc_attr fail_sunrpc = {
.attr = FAULT_ATTR_INITIALIZER,
};
EXPORT_SYMBOL_GPL(fail_sunrpc);
#endif #endif
void __exit void __exit
...@@ -318,21 +278,11 @@ sunrpc_debugfs_exit(void) ...@@ -318,21 +278,11 @@ sunrpc_debugfs_exit(void)
void __init void __init
sunrpc_debugfs_init(void) sunrpc_debugfs_init(void)
{ {
struct dentry *rpc_fault_dir;
topdir = debugfs_create_dir("sunrpc", NULL); topdir = debugfs_create_dir("sunrpc", NULL);
rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
rpc_fault_dir = debugfs_create_dir("inject_fault", topdir); fail_sunrpc_init();
debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL,
&fault_disconnect_fops);
#if IS_ENABLED(CONFIG_FAIL_SUNRPC)
fault_create_debugfs_attr("fail_sunrpc", NULL,
&fail_sunrpc.attr);
#endif
} }
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
struct fail_sunrpc_attr { struct fail_sunrpc_attr {
struct fault_attr attr; struct fault_attr attr;
bool ignore_client_disconnect;
}; };
extern struct fail_sunrpc_attr fail_sunrpc; extern struct fail_sunrpc_attr fail_sunrpc;
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include "sunrpc.h" #include "sunrpc.h"
#include "sysfs.h" #include "sysfs.h"
#include "fail.h"
/* /*
* Local variables * Local variables
...@@ -855,6 +856,19 @@ xprt_init_autodisconnect(struct timer_list *t) ...@@ -855,6 +856,19 @@ xprt_init_autodisconnect(struct timer_list *t)
queue_work(xprtiod_workqueue, &xprt->task_cleanup); queue_work(xprtiod_workqueue, &xprt->task_cleanup);
} }
#if IS_ENABLED(CONFIG_FAIL_SUNRPC)
static void xprt_inject_disconnect(struct rpc_xprt *xprt)
{
if (!fail_sunrpc.ignore_client_disconnect &&
should_fail(&fail_sunrpc.attr, 1))
xprt->ops->inject_disconnect(xprt);
}
#else
static inline void xprt_inject_disconnect(struct rpc_xprt *xprt)
{
}
#endif
bool xprt_lock_connect(struct rpc_xprt *xprt, bool xprt_lock_connect(struct rpc_xprt *xprt,
struct rpc_task *task, struct rpc_task *task,
void *cookie) void *cookie)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment