mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	fault-inject: allow configuration via configfs
This provides a helper function to allow configuration of fault-injection for configfs-based drivers. The config items created by this function have the same interface as the one created under debugfs by fault_create_debugfs_attr(). Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Link: https://lore.kernel.org/r/20230327143733.14599-2-akinobu.mita@gmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
		
							parent
							
								
									4d5bba5bee
								
							
						
					
					
						commit
						4668c7a294
					
				
					 3 changed files with 225 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/debugfs.h>
 | 
			
		||||
#include <linux/configfs.h>
 | 
			
		||||
#include <linux/ratelimit.h>
 | 
			
		||||
#include <linux/atomic.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +66,27 @@ static inline struct dentry *fault_create_debugfs_attr(const char *name,
 | 
			
		|||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FAULT_INJECTION_CONFIGFS
 | 
			
		||||
 | 
			
		||||
struct fault_config {
 | 
			
		||||
	struct fault_attr attr;
 | 
			
		||||
	struct config_group group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void fault_config_init(struct fault_config *config, const char *name);
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_FAULT_INJECTION_CONFIGFS */
 | 
			
		||||
 | 
			
		||||
struct fault_config {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void fault_config_init(struct fault_config *config,
 | 
			
		||||
			const char *name)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_CONFIGFS */
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION */
 | 
			
		||||
 | 
			
		||||
struct kmem_cache;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1958,9 +1958,20 @@ config FAIL_SUNRPC
 | 
			
		|||
	  Provide fault-injection capability for SunRPC and
 | 
			
		||||
	  its consumers.
 | 
			
		||||
 | 
			
		||||
config FAULT_INJECTION_CONFIGFS
 | 
			
		||||
	bool "Configfs interface for fault-injection capabilities"
 | 
			
		||||
	depends on FAULT_INJECTION && CONFIGFS_FS
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows configfs-based drivers to dynamically configure
 | 
			
		||||
	  fault-injection via configfs.  Each parameter for driver-specific
 | 
			
		||||
	  fault-injection can be made visible as a configfs attribute in a
 | 
			
		||||
	  configfs group.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
config FAULT_INJECTION_STACKTRACE_FILTER
 | 
			
		||||
	bool "stacktrace filter for fault-injection capabilities"
 | 
			
		||||
	depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT
 | 
			
		||||
	depends on FAULT_INJECTION
 | 
			
		||||
	depends on (FAULT_INJECTION_DEBUG_FS || FAULT_INJECTION_CONFIGFS) && STACKTRACE_SUPPORT
 | 
			
		||||
	select STACKTRACE
 | 
			
		||||
	depends on FRAME_POINTER || MIPS || PPC || S390 || MICROBLAZE || ARM || ARC || X86
 | 
			
		||||
	help
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -244,3 +244,194 @@ struct dentry *fault_create_debugfs_attr(const char *name,
 | 
			
		|||
EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FAULT_INJECTION_CONFIGFS
 | 
			
		||||
 | 
			
		||||
/* These configfs attribute utilities are copied from drivers/block/null_blk/main.c */
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_uint_attr_show(unsigned int val, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(page, PAGE_SIZE, "%u\n", val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_ulong_attr_show(unsigned long val, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(page, PAGE_SIZE, "%lu\n", val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_bool_attr_show(bool val, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(page, PAGE_SIZE, "%u\n", val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_atomic_t_attr_show(atomic_t val, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(page, PAGE_SIZE, "%d\n", atomic_read(&val));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_uint_attr_store(unsigned int *val, const char *page, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int tmp;
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	result = kstrtouint(page, 0, &tmp);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
	*val = tmp;
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_ulong_attr_store(unsigned long *val, const char *page, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	int result;
 | 
			
		||||
	unsigned long tmp;
 | 
			
		||||
 | 
			
		||||
	result = kstrtoul(page, 0, &tmp);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
	*val = tmp;
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_bool_attr_store(bool *val, const char *page, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	bool tmp;
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	result = kstrtobool(page, &tmp);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
	*val = tmp;
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_atomic_t_attr_store(atomic_t *val, const char *page, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	int tmp;
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	result = kstrtoint(page, 0, &tmp);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
	atomic_set(val, tmp);
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CONFIGFS_ATTR_NAMED(_pfx, _name, _attr_name)	\
 | 
			
		||||
static struct configfs_attribute _pfx##attr_##_name = {	\
 | 
			
		||||
	.ca_name	= _attr_name,			\
 | 
			
		||||
	.ca_mode	= 0644,				\
 | 
			
		||||
	.ca_owner	= THIS_MODULE,			\
 | 
			
		||||
	.show		= _pfx##_name##_show,		\
 | 
			
		||||
	.store		= _pfx##_name##_store,		\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct fault_config *to_fault_config(struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(to_config_group(item), struct fault_config, group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define FAULT_CONFIGFS_ATTR_NAMED(NAME, ATTR_NAME, MEMBER, TYPE)				\
 | 
			
		||||
static ssize_t fault_##NAME##_show(struct config_item *item, char *page)			\
 | 
			
		||||
{												\
 | 
			
		||||
	return fault_##TYPE##_attr_show(to_fault_config(item)->attr.MEMBER, page);		\
 | 
			
		||||
}												\
 | 
			
		||||
static ssize_t fault_##NAME##_store(struct config_item *item, const char *page, size_t count)	\
 | 
			
		||||
{												\
 | 
			
		||||
	struct fault_config *config = to_fault_config(item);					\
 | 
			
		||||
	return fault_##TYPE##_attr_store(&config->attr.MEMBER, page, count);			\
 | 
			
		||||
}												\
 | 
			
		||||
CONFIGFS_ATTR_NAMED(fault_, NAME, ATTR_NAME)
 | 
			
		||||
 | 
			
		||||
#define FAULT_CONFIGFS_ATTR(NAME, TYPE)	\
 | 
			
		||||
	FAULT_CONFIGFS_ATTR_NAMED(NAME, __stringify(NAME), NAME, TYPE)
 | 
			
		||||
 | 
			
		||||
FAULT_CONFIGFS_ATTR(probability, ulong);
 | 
			
		||||
FAULT_CONFIGFS_ATTR(interval, ulong);
 | 
			
		||||
FAULT_CONFIGFS_ATTR(times, atomic_t);
 | 
			
		||||
FAULT_CONFIGFS_ATTR(space, atomic_t);
 | 
			
		||||
FAULT_CONFIGFS_ATTR(verbose, ulong);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(ratelimit_interval, "verbose_ratelimit_interval_ms",
 | 
			
		||||
		ratelimit_state.interval, uint);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(ratelimit_burst, "verbose_ratelimit_burst",
 | 
			
		||||
		ratelimit_state.burst, uint);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(task_filter, "task-filter", task_filter, bool);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_stacktrace_depth_show(struct config_item *item, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return fault_ulong_attr_show(to_fault_config(item)->attr.stacktrace_depth, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_stacktrace_depth_store(struct config_item *item, const char *page,
 | 
			
		||||
		size_t count)
 | 
			
		||||
{
 | 
			
		||||
	int result;
 | 
			
		||||
	unsigned long tmp;
 | 
			
		||||
 | 
			
		||||
	result = kstrtoul(page, 0, &tmp);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
 | 
			
		||||
	to_fault_config(item)->attr.stacktrace_depth =
 | 
			
		||||
		min_t(unsigned long, tmp, MAX_STACK_TRACE_DEPTH);
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIGFS_ATTR_NAMED(fault_, stacktrace_depth, "stacktrace-depth");
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_xul_attr_show(unsigned long val, char *page)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(page, PAGE_SIZE,
 | 
			
		||||
			sizeof(val) == sizeof(u32) ? "0x%08lx\n" : "0x%016lx\n", val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fault_xul_attr_store(unsigned long *val, const char *page, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	return fault_ulong_attr_store(val, page, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(require_start, "require-start", require_start, xul);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(require_end, "require-end", require_end, xul);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(reject_start, "reject-start", reject_start, xul);
 | 
			
		||||
FAULT_CONFIGFS_ATTR_NAMED(reject_end, "reject-end", reject_end, xul);
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 | 
			
		||||
 | 
			
		||||
static struct configfs_attribute *fault_config_attrs[] = {
 | 
			
		||||
	&fault_attr_probability,
 | 
			
		||||
	&fault_attr_interval,
 | 
			
		||||
	&fault_attr_times,
 | 
			
		||||
	&fault_attr_space,
 | 
			
		||||
	&fault_attr_verbose,
 | 
			
		||||
	&fault_attr_ratelimit_interval,
 | 
			
		||||
	&fault_attr_ratelimit_burst,
 | 
			
		||||
	&fault_attr_task_filter,
 | 
			
		||||
#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 | 
			
		||||
	&fault_attr_stacktrace_depth,
 | 
			
		||||
	&fault_attr_require_start,
 | 
			
		||||
	&fault_attr_require_end,
 | 
			
		||||
	&fault_attr_reject_start,
 | 
			
		||||
	&fault_attr_reject_end,
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct config_item_type fault_config_type = {
 | 
			
		||||
	.ct_attrs	= fault_config_attrs,
 | 
			
		||||
	.ct_owner	= THIS_MODULE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void fault_config_init(struct fault_config *config, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	config_group_init_type_name(&config->group, name, &fault_config_type);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(fault_config_init);
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_FAULT_INJECTION_CONFIGFS */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue