forked from mirrors/linux
		
	[PATCH] fault-injection capabilities infrastructure
This patch provides base functions implement to fault-injection capabilities. - The function should_fail() is taken from failmalloc-1.0 (http://www.nongnu.org/failmalloc/) [akpm@osdl.org: cleanups, comments, add __init] Cc: <okuji@enbug.org> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Signed-off-by: Don Mullis <dwm@meer.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
		
							parent
							
								
									de1ba09b21
								
							
						
					
					
						commit
						6ff1cb355e
					
				
					 4 changed files with 265 additions and 0 deletions
				
			
		
							
								
								
									
										69
									
								
								include/linux/fault-inject.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								include/linux/fault-inject.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | #ifndef _LINUX_FAULT_INJECT_H | ||||||
|  | #define _LINUX_FAULT_INJECT_H | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FAULT_INJECTION | ||||||
|  | 
 | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/debugfs.h> | ||||||
|  | #include <asm/atomic.h> | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * For explanation of the elements of this struct, see | ||||||
|  |  * Documentation/fault-injection/fault-injection.txt | ||||||
|  |  */ | ||||||
|  | struct fault_attr { | ||||||
|  | 	unsigned long probability; | ||||||
|  | 	unsigned long interval; | ||||||
|  | 	atomic_t times; | ||||||
|  | 	atomic_t space; | ||||||
|  | 	unsigned long verbose; | ||||||
|  | 
 | ||||||
|  | 	unsigned long count; | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||||||
|  | 
 | ||||||
|  | 	struct { | ||||||
|  | 		struct dentry *dir; | ||||||
|  | 
 | ||||||
|  | 		struct dentry *probability_file; | ||||||
|  | 		struct dentry *interval_file; | ||||||
|  | 		struct dentry *times_file; | ||||||
|  | 		struct dentry *space_file; | ||||||
|  | 		struct dentry *verbose_file; | ||||||
|  | 	} dentries; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define FAULT_ATTR_INITIALIZER {				\ | ||||||
|  | 		.interval = 1,					\ | ||||||
|  | 		.times = ATOMIC_INIT(1),			\ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER | ||||||
|  | int setup_fault_attr(struct fault_attr *attr, char *str); | ||||||
|  | void should_fail_srandom(unsigned long entropy); | ||||||
|  | int should_fail(struct fault_attr *attr, ssize_t size); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||||||
|  | 
 | ||||||
|  | int init_fault_attr_dentries(struct fault_attr *attr, const char *name); | ||||||
|  | void cleanup_fault_attr_dentries(struct fault_attr *attr); | ||||||
|  | 
 | ||||||
|  | #else /* CONFIG_FAULT_INJECTION_DEBUG_FS */ | ||||||
|  | 
 | ||||||
|  | static inline int init_fault_attr_dentries(struct fault_attr *attr, | ||||||
|  | 					  const char *name) | ||||||
|  | { | ||||||
|  | 	return -ENODEV; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void cleanup_fault_attr_dentries(struct fault_attr *attr) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_FAULT_INJECTION */ | ||||||
|  | 
 | ||||||
|  | #endif /* _LINUX_FAULT_INJECT_H */ | ||||||
|  | @ -413,3 +413,15 @@ config LKDTM | ||||||
| 
 | 
 | ||||||
| 	Documentation on how to use the module can be found in | 	Documentation on how to use the module can be found in | ||||||
| 	drivers/misc/lkdtm.c | 	drivers/misc/lkdtm.c | ||||||
|  | 
 | ||||||
|  | config FAULT_INJECTION | ||||||
|  | 	bool | ||||||
|  | 
 | ||||||
|  | config FAULT_INJECTION_DEBUG_FS | ||||||
|  | 	bool "Debugfs entries for fault-injection capabilities" | ||||||
|  | 	depends on FAULT_INJECTION && SYSFS | ||||||
|  | 	select DEBUG_FS | ||||||
|  | 	help | ||||||
|  | 	  This option enable configuration of fault-injection capabilities via | ||||||
|  | 	  debugfs. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -55,6 +55,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o | ||||||
| obj-$(CONFIG_AUDIT_GENERIC) += audit.o | obj-$(CONFIG_AUDIT_GENERIC) += audit.o | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_SWIOTLB) += swiotlb.o | obj-$(CONFIG_SWIOTLB) += swiotlb.o | ||||||
|  | obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o | ||||||
| 
 | 
 | ||||||
| lib-$(CONFIG_GENERIC_BUG) += bug.o | lib-$(CONFIG_GENERIC_BUG) += bug.o | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										183
									
								
								lib/fault-inject.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								lib/fault-inject.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,183 @@ | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/random.h> | ||||||
|  | #include <linux/stat.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/fs.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/fault-inject.h> | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * setup_fault_attr() is a helper function for various __setup handlers, so it | ||||||
|  |  * returns 0 on error, because that is what __setup handlers do. | ||||||
|  |  */ | ||||||
|  | int __init setup_fault_attr(struct fault_attr *attr, char *str) | ||||||
|  | { | ||||||
|  | 	unsigned long probability; | ||||||
|  | 	unsigned long interval; | ||||||
|  | 	int times; | ||||||
|  | 	int space; | ||||||
|  | 
 | ||||||
|  | 	/* "<interval>,<probability>,<space>,<times>" */ | ||||||
|  | 	if (sscanf(str, "%lu,%lu,%d,%d", | ||||||
|  | 			&interval, &probability, &space, ×) < 4) { | ||||||
|  | 		printk(KERN_WARNING | ||||||
|  | 			"FAULT_INJECTION: failed to parse arguments\n"); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	attr->probability = probability; | ||||||
|  | 	attr->interval = interval; | ||||||
|  | 	atomic_set(&attr->times, times); | ||||||
|  | 	atomic_set(&attr->space, space); | ||||||
|  | 
 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void fail_dump(struct fault_attr *attr) | ||||||
|  | { | ||||||
|  | 	if (attr->verbose > 0) | ||||||
|  | 		printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure\n"); | ||||||
|  | 	if (attr->verbose > 1) | ||||||
|  | 		dump_stack(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define atomic_dec_not_zero(v)		atomic_add_unless((v), -1, 0) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This code is stolen from failmalloc-1.0 | ||||||
|  |  * http://www.nongnu.org/failmalloc/
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | int should_fail(struct fault_attr *attr, ssize_t size) | ||||||
|  | { | ||||||
|  | 	if (atomic_read(&attr->times) == 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	if (atomic_read(&attr->space) > size) { | ||||||
|  | 		atomic_sub(size, &attr->space); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (attr->interval > 1) { | ||||||
|  | 		attr->count++; | ||||||
|  | 		if (attr->count % attr->interval) | ||||||
|  | 			return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (attr->probability > random32() % 100) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | 
 | ||||||
|  | fail: | ||||||
|  | 	fail_dump(attr); | ||||||
|  | 
 | ||||||
|  | 	if (atomic_read(&attr->times) != -1) | ||||||
|  | 		atomic_dec_not_zero(&attr->times); | ||||||
|  | 
 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||||||
|  | 
 | ||||||
|  | static void debugfs_ul_set(void *data, u64 val) | ||||||
|  | { | ||||||
|  | 	*(unsigned long *)data = val; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u64 debugfs_ul_get(void *data) | ||||||
|  | { | ||||||
|  | 	return *(unsigned long *)data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DEFINE_SIMPLE_ATTRIBUTE(fops_ul, debugfs_ul_get, debugfs_ul_set, "%llu\n"); | ||||||
|  | 
 | ||||||
|  | static struct dentry *debugfs_create_ul(const char *name, mode_t mode, | ||||||
|  | 				struct dentry *parent, unsigned long *value) | ||||||
|  | { | ||||||
|  | 	return debugfs_create_file(name, mode, parent, value, &fops_ul); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void debugfs_atomic_t_set(void *data, u64 val) | ||||||
|  | { | ||||||
|  | 	atomic_set((atomic_t *)data, val); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u64 debugfs_atomic_t_get(void *data) | ||||||
|  | { | ||||||
|  | 	return atomic_read((atomic_t *)data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get, | ||||||
|  | 			debugfs_atomic_t_set, "%lld\n"); | ||||||
|  | 
 | ||||||
|  | static struct dentry *debugfs_create_atomic_t(const char *name, mode_t mode, | ||||||
|  | 				struct dentry *parent, atomic_t *value) | ||||||
|  | { | ||||||
|  | 	return debugfs_create_file(name, mode, parent, value, &fops_atomic_t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void cleanup_fault_attr_dentries(struct fault_attr *attr) | ||||||
|  | { | ||||||
|  | 	debugfs_remove(attr->dentries.probability_file); | ||||||
|  | 	attr->dentries.probability_file = NULL; | ||||||
|  | 
 | ||||||
|  | 	debugfs_remove(attr->dentries.interval_file); | ||||||
|  | 	attr->dentries.interval_file = NULL; | ||||||
|  | 
 | ||||||
|  | 	debugfs_remove(attr->dentries.times_file); | ||||||
|  | 	attr->dentries.times_file = NULL; | ||||||
|  | 
 | ||||||
|  | 	debugfs_remove(attr->dentries.space_file); | ||||||
|  | 	attr->dentries.space_file = NULL; | ||||||
|  | 
 | ||||||
|  | 	debugfs_remove(attr->dentries.verbose_file); | ||||||
|  | 	attr->dentries.verbose_file = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (attr->dentries.dir) | ||||||
|  | 		WARN_ON(!simple_empty(attr->dentries.dir)); | ||||||
|  | 
 | ||||||
|  | 	debugfs_remove(attr->dentries.dir); | ||||||
|  | 	attr->dentries.dir = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int init_fault_attr_dentries(struct fault_attr *attr, const char *name) | ||||||
|  | { | ||||||
|  | 	mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; | ||||||
|  | 	struct dentry *dir; | ||||||
|  | 
 | ||||||
|  | 	memset(&attr->dentries, 0, sizeof(attr->dentries)); | ||||||
|  | 
 | ||||||
|  | 	dir = debugfs_create_dir(name, NULL); | ||||||
|  | 	if (!dir) | ||||||
|  | 		goto fail; | ||||||
|  | 	attr->dentries.dir = dir; | ||||||
|  | 
 | ||||||
|  | 	attr->dentries.probability_file = | ||||||
|  | 		debugfs_create_ul("probability", mode, dir, &attr->probability); | ||||||
|  | 
 | ||||||
|  | 	attr->dentries.interval_file = | ||||||
|  | 		debugfs_create_ul("interval", mode, dir, &attr->interval); | ||||||
|  | 
 | ||||||
|  | 	attr->dentries.times_file = | ||||||
|  | 		debugfs_create_atomic_t("times", mode, dir, &attr->times); | ||||||
|  | 
 | ||||||
|  | 	attr->dentries.space_file = | ||||||
|  | 		debugfs_create_atomic_t("space", mode, dir, &attr->space); | ||||||
|  | 
 | ||||||
|  | 	attr->dentries.verbose_file = | ||||||
|  | 		debugfs_create_ul("verbose", mode, dir, &attr->verbose); | ||||||
|  | 
 | ||||||
|  | 	if (!attr->dentries.probability_file || !attr->dentries.interval_file | ||||||
|  | 	    || !attr->dentries.times_file || !attr->dentries.space_file | ||||||
|  | 	    || !attr->dentries.verbose_file) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | fail: | ||||||
|  | 	cleanup_fault_attr_dentries(attr); | ||||||
|  | 	return -ENOMEM; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ | ||||||
		Loading…
	
		Reference in a new issue
	
	 Akinobu Mita
						Akinobu Mita