From: Darrick J. Wong <djwong@xxxxxxxxxx> Use static keys so that we can configure debugging assertions and dmesg warnings at runtime. By default this is turned off so the cost is merely scanning a nop sled. However, fuse server developers can turn it on for their debugging systems. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/fuse_i.h | 8 +++++ fs/fuse/iomap_priv.h | 16 ++++++++-- fs/fuse/Kconfig | 15 +++++++++ fs/fuse/file_iomap.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/inode.c | 7 ++++ 5 files changed, 124 insertions(+), 3 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index b28054c254f866..2cd9f4cdc6a7ef 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1607,6 +1607,14 @@ extern void fuse_sysctl_unregister(void); #define fuse_sysctl_unregister() do { } while (0) #endif /* CONFIG_SYSCTL */ +#if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG) +int fuse_iomap_sysfs_init(struct kobject *kobj); +void fuse_iomap_sysfs_cleanup(struct kobject *kobj); +#else +# define fuse_iomap_sysfs_init(...) (0) +# define fuse_iomap_sysfs_cleanup(...) ((void)0) +#endif + #if IS_ENABLED(CONFIG_FUSE_IOMAP) bool fuse_iomap_enabled(void); diff --git a/fs/fuse/iomap_priv.h b/fs/fuse/iomap_priv.h index ca8544a95a4267..7002eb38f87fe1 100644 --- a/fs/fuse/iomap_priv.h +++ b/fs/fuse/iomap_priv.h @@ -6,19 +6,29 @@ #ifndef _FS_FUSE_IOMAP_PRIV_H #define _FS_FUSE_IOMAP_PRIV_H +#if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG_DEFAULT) +DECLARE_STATIC_KEY_TRUE(fuse_iomap_debug); +#else +DECLARE_STATIC_KEY_FALSE(fuse_iomap_debug); +#endif + #if IS_ENABLED(CONFIG_FUSE_IOMAP) #if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG) -# define ASSERT(condition) do { \ +# define ASSERT(condition) \ +while (static_branch_unlikely(&fuse_iomap_debug)) { \ int __cond = !!(condition); \ if (unlikely(!__cond)) \ trace_fuse_iomap_assert(__func__, __LINE__, #condition); \ WARN(!__cond, "Assertion failed: %s, func: %s, line: %d", #condition, __func__, __LINE__); \ -} while (0) + break; \ +} # define BAD_DATA(condition) ({ \ int __cond = !!(condition); \ if (unlikely(__cond)) \ trace_fuse_iomap_bad_data(__func__, __LINE__, #condition); \ - WARN(__cond, "Bad mapping: %s, func: %s, line: %d", #condition, __func__, __LINE__); \ + if (static_branch_unlikely(&fuse_iomap_debug)) \ + WARN(__cond, "Bad mapping: %s, func: %s, line: %d", #condition, __func__, __LINE__); \ + unlikely(__cond); \ }) #else # define ASSERT(condition) diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index e0bcbd42431344..6be74396ef5198 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -90,6 +90,21 @@ config FUSE_IOMAP_DEBUG Enable debugging assertions for the fuse iomap code paths and logging of bad iomap file mapping data being sent to the kernel. + Say N here if you don't want any debugging code code compiled in at + all. + +config FUSE_IOMAP_DEBUG_BY_DEFAULT + bool "Debug FUSE file IO over iomap at boot time" + default n + depends on FUSE_IOMAP_DEBUG + help + At boot time, enable debugging assertions for the fuse iomap code + paths and warnings about bad iomap file mapping data. This enables + fuse server authors to control debugging at runtime even on a + distribution kernel while avoiding most of the overhead on production + systems. The setting can be changed at runtime via + /sys/fs/fuse/iomap/debug. + config FUSE_IO_URING bool "FUSE communication over io-uring" default y diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c index d11b1f810523fc..fad5457d669baf 100644 --- a/fs/fuse/file_iomap.c +++ b/fs/fuse/file_iomap.c @@ -8,6 +8,12 @@ #include "fuse_trace.h" #include "iomap_priv.h" +#if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG_DEFAULT) +DEFINE_STATIC_KEY_TRUE(fuse_iomap_debug); +#else +DEFINE_STATIC_KEY_FALSE(fuse_iomap_debug); +#endif + static bool __read_mostly enable_iomap = #if IS_ENABLED(CONFIG_FUSE_IOMAP_BY_DEFAULT) true; @@ -17,6 +23,81 @@ static bool __read_mostly enable_iomap = module_param(enable_iomap, bool, 0644); MODULE_PARM_DESC(enable_iomap, "Enable file I/O through iomap"); +#if IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG) +static struct kobject *iomap_kobj; + +static ssize_t fuse_iomap_debug_show(struct kobject *kobject, + struct kobj_attribute *a, char *buf) +{ + return sysfs_emit(buf, "%d\n", !!static_key_enabled(&fuse_iomap_debug)); +} + +static ssize_t fuse_iomap_debug_store(struct kobject *kobject, + struct kobj_attribute *a, + const char *buf, size_t count) +{ + int ret; + int val; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (val) + static_branch_enable(&fuse_iomap_debug); + else + static_branch_disable(&fuse_iomap_debug); + + return count; +} + +#define __INIT_KOBJ_ATTR(_name, _mode, _show, _store) \ +{ \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define FUSE_ATTR_RW(_name, _show, _store) \ + static struct kobj_attribute fuse_attr_##_name = \ + __INIT_KOBJ_ATTR(_name, 0644, _show, _store) + +#define FUSE_ATTR_PTR(_name) \ + (&fuse_attr_##_name.attr) + +FUSE_ATTR_RW(debug, fuse_iomap_debug_show, fuse_iomap_debug_store); + +static const struct attribute *fuse_iomap_attrs[] = { + FUSE_ATTR_PTR(debug), + NULL, +}; + +int fuse_iomap_sysfs_init(struct kobject *fuse_kobj) +{ + int error; + + iomap_kobj = kobject_create_and_add("iomap", fuse_kobj); + if (!iomap_kobj) + return -ENOMEM; + + error = sysfs_create_files(iomap_kobj, fuse_iomap_attrs); + if (error) { + kobject_put(iomap_kobj); + return error; + } + + return 0; +} + +void fuse_iomap_sysfs_cleanup(struct kobject *fuse_kobj) +{ + kobject_put(iomap_kobj); +} +#endif /* IS_ENABLED(CONFIG_FUSE_IOMAP_DEBUG) */ + bool fuse_iomap_enabled(void) { /* Don't let anyone touch iomap until the end of the patchset. */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 82e074642e8e9b..9448a11c828fef 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -2217,8 +2217,14 @@ static int fuse_sysfs_init(void) if (err) goto out_fuse_unregister; + err = fuse_iomap_sysfs_init(fuse_kobj); + if (err) + goto out_fuse_connections; + return 0; + out_fuse_connections: + sysfs_remove_mount_point(fuse_kobj, "connections"); out_fuse_unregister: kobject_put(fuse_kobj); out_err: @@ -2227,6 +2233,7 @@ static int fuse_sysfs_init(void) static void fuse_sysfs_cleanup(void) { + fuse_iomap_sysfs_cleanup(fuse_kobj); sysfs_remove_mount_point(fuse_kobj, "connections"); kobject_put(fuse_kobj); }