Add support for injecting ENOSPC or EIO errors. This needs to be enabled by CONFIG_CACHEFILES_ERROR_INJECTION=y. Once enabled, ENOSPC on things like write and mkdir can be triggered by: echo 1 >/proc/sys/cachefiles/error_injection and EIO can be triggered on most operations by: echo 2 >/proc/sys/cachefiles/error_injection Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: linux-cachefs@xxxxxxxxxx Link: https://lore.kernel.org/r/163819624706.215744.6911916249119962943.stgit@xxxxxxxxxxxxxxxxxxxxxx/ # v1 --- fs/cachefiles/Kconfig | 7 ++++++ fs/cachefiles/Makefile | 2 ++ fs/cachefiles/error_inject.c | 46 ++++++++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 42 +++++++++++++++++++++++++++++++++++++- fs/cachefiles/main.c | 12 +++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/error_inject.c diff --git a/fs/cachefiles/Kconfig b/fs/cachefiles/Kconfig index 6827b40f7ddc..719faeeda168 100644 --- a/fs/cachefiles/Kconfig +++ b/fs/cachefiles/Kconfig @@ -19,3 +19,10 @@ config CACHEFILES_DEBUG caching on files module. If this is set, the debugging output may be enabled by setting bits in /sys/modules/cachefiles/parameter/debug or by including a debugging specifier in /etc/cachefilesd.conf. + +config CACHEFILES_ERROR_INJECTION + bool "Provide error injection for cachefiles" + depends on CACHEFILES && SYSCTL + help + This permits error injection to be enabled in cachefiles whilst a + cache is in service. diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index a7f3e982e249..183fb5f3b8b1 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -6,4 +6,6 @@ cachefiles-y := \ main.o +cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o + obj-$(CONFIG_CACHEFILES) := cachefiles.o diff --git a/fs/cachefiles/error_inject.c b/fs/cachefiles/error_inject.c new file mode 100644 index 000000000000..58f8aec964e4 --- /dev/null +++ b/fs/cachefiles/error_inject.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Error injection handling. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@xxxxxxxxxx) + */ + +#include <linux/sysctl.h> +#include "internal.h" + +unsigned int cachefiles_error_injection_state; + +static struct ctl_table_header *cachefiles_sysctl; +static struct ctl_table cachefiles_sysctls[] = { + { + .procname = "error_injection", + .data = &cachefiles_error_injection_state, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec, + }, + {} +}; + +static struct ctl_table cachefiles_sysctls_root[] = { + { + .procname = "cachefiles", + .mode = 0555, + .child = cachefiles_sysctls, + }, + {} +}; + +int __init cachefiles_register_error_injection(void) +{ + cachefiles_sysctl = register_sysctl_table(cachefiles_sysctls_root); + if (!cachefiles_sysctl) + return -ENOMEM; + return 0; + +} + +void cachefiles_unregister_error_injection(void) +{ + unregister_sysctl_table(cachefiles_sysctl); +} diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index cff4b2a5f928..1f2fea902d3e 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -64,7 +64,47 @@ struct cachefiles_cache { /* - * Debug tracing. + * error_inject.c + */ +#ifdef CONFIG_CACHEFILES_ERROR_INJECTION +extern unsigned int cachefiles_error_injection_state; +extern int cachefiles_register_error_injection(void); +extern void cachefiles_unregister_error_injection(void); + +#else +#define cachefiles_error_injection_state 0 + +static inline int cachefiles_register_error_injection(void) +{ + return 0; +} + +static inline void cachefiles_unregister_error_injection(void) +{ +} +#endif + + +static inline int cachefiles_inject_read_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : 0; +} + +static inline int cachefiles_inject_write_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : + cachefiles_error_injection_state & 1 ? -ENOSPC : + 0; +} + +static inline int cachefiles_inject_remove_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : 0; +} + + +/* + * Debug tracing */ extern unsigned cachefiles_debug; #define CACHEFILES_DEBUG_KENTER 1 diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c index 47bc1cc078de..387d42c7185f 100644 --- a/fs/cachefiles/main.c +++ b/fs/cachefiles/main.c @@ -36,8 +36,18 @@ MODULE_LICENSE("GPL"); */ static int __init cachefiles_init(void) { + int ret; + + ret = cachefiles_register_error_injection(); + if (ret < 0) + goto error_einj; + pr_info("Loaded\n"); return 0; + +error_einj: + pr_err("failed to register: %d\n", ret); + return ret; } fs_initcall(cachefiles_init); @@ -48,6 +58,8 @@ fs_initcall(cachefiles_init); static void __exit cachefiles_exit(void) { pr_info("Unloading\n"); + + cachefiles_unregister_error_injection(); } module_exit(cachefiles_exit);