diff options
| -rw-r--r-- | include/linux/fault-inject.h | 9 | ||||
| -rw-r--r-- | lib/Kconfig.debug | 1 | ||||
| -rw-r--r-- | mm/Makefile | 1 | ||||
| -rw-r--r-- | mm/failslab.c | 59 | ||||
| -rw-r--r-- | mm/slab.c | 75 | ||||
| -rw-r--r-- | mm/slub.c | 4 |
6 files changed, 79 insertions, 70 deletions
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 32368c4f0326..06ca9b21dad2 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h | |||
| @@ -81,4 +81,13 @@ static inline void cleanup_fault_attr_dentries(struct fault_attr *attr) | |||
| 81 | 81 | ||
| 82 | #endif /* CONFIG_FAULT_INJECTION */ | 82 | #endif /* CONFIG_FAULT_INJECTION */ |
| 83 | 83 | ||
| 84 | #ifdef CONFIG_FAILSLAB | ||
| 85 | extern bool should_failslab(size_t size, gfp_t gfpflags); | ||
| 86 | #else | ||
| 87 | static inline bool should_failslab(size_t size, gfp_t gfpflags) | ||
| 88 | { | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | #endif /* CONFIG_FAILSLAB */ | ||
| 92 | |||
| 84 | #endif /* _LINUX_FAULT_INJECT_H */ | 93 | #endif /* _LINUX_FAULT_INJECT_H */ |
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index b0f239e443bc..af65ae7f0549 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
| @@ -699,6 +699,7 @@ config FAULT_INJECTION | |||
| 699 | config FAILSLAB | 699 | config FAILSLAB |
| 700 | bool "Fault-injection capability for kmalloc" | 700 | bool "Fault-injection capability for kmalloc" |
| 701 | depends on FAULT_INJECTION | 701 | depends on FAULT_INJECTION |
| 702 | depends on SLAB || SLUB | ||
| 702 | help | 703 | help |
| 703 | Provide fault-injection capability for kmalloc. | 704 | Provide fault-injection capability for kmalloc. |
| 704 | 705 | ||
diff --git a/mm/Makefile b/mm/Makefile index c06b45a1ff5f..51c27709cc7c 100644 --- a/mm/Makefile +++ b/mm/Makefile | |||
| @@ -28,6 +28,7 @@ obj-$(CONFIG_SLOB) += slob.o | |||
| 28 | obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o | 28 | obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o |
| 29 | obj-$(CONFIG_SLAB) += slab.o | 29 | obj-$(CONFIG_SLAB) += slab.o |
| 30 | obj-$(CONFIG_SLUB) += slub.o | 30 | obj-$(CONFIG_SLUB) += slub.o |
| 31 | obj-$(CONFIG_FAILSLAB) += failslab.o | ||
| 31 | obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o | 32 | obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o |
| 32 | obj-$(CONFIG_FS_XIP) += filemap_xip.o | 33 | obj-$(CONFIG_FS_XIP) += filemap_xip.o |
| 33 | obj-$(CONFIG_MIGRATION) += migrate.o | 34 | obj-$(CONFIG_MIGRATION) += migrate.o |
diff --git a/mm/failslab.c b/mm/failslab.c new file mode 100644 index 000000000000..7c6ea6493f80 --- /dev/null +++ b/mm/failslab.c | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #include <linux/fault-inject.h> | ||
| 2 | |||
| 3 | static struct { | ||
| 4 | struct fault_attr attr; | ||
| 5 | u32 ignore_gfp_wait; | ||
| 6 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||
| 7 | struct dentry *ignore_gfp_wait_file; | ||
| 8 | #endif | ||
| 9 | } failslab = { | ||
| 10 | .attr = FAULT_ATTR_INITIALIZER, | ||
| 11 | .ignore_gfp_wait = 1, | ||
| 12 | }; | ||
| 13 | |||
| 14 | bool should_failslab(size_t size, gfp_t gfpflags) | ||
| 15 | { | ||
| 16 | if (gfpflags & __GFP_NOFAIL) | ||
| 17 | return false; | ||
| 18 | |||
| 19 | if (failslab.ignore_gfp_wait && (gfpflags & __GFP_WAIT)) | ||
| 20 | return false; | ||
| 21 | |||
| 22 | return should_fail(&failslab.attr, size); | ||
| 23 | } | ||
| 24 | |||
| 25 | static int __init setup_failslab(char *str) | ||
| 26 | { | ||
| 27 | return setup_fault_attr(&failslab.attr, str); | ||
| 28 | } | ||
| 29 | __setup("failslab=", setup_failslab); | ||
| 30 | |||
| 31 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||
| 32 | |||
| 33 | static int __init failslab_debugfs_init(void) | ||
| 34 | { | ||
| 35 | mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; | ||
| 36 | struct dentry *dir; | ||
| 37 | int err; | ||
| 38 | |||
| 39 | err = init_fault_attr_dentries(&failslab.attr, "failslab"); | ||
| 40 | if (err) | ||
| 41 | return err; | ||
| 42 | dir = failslab.attr.dentries.dir; | ||
| 43 | |||
| 44 | failslab.ignore_gfp_wait_file = | ||
| 45 | debugfs_create_bool("ignore-gfp-wait", mode, dir, | ||
| 46 | &failslab.ignore_gfp_wait); | ||
| 47 | |||
| 48 | if (!failslab.ignore_gfp_wait_file) { | ||
| 49 | err = -ENOMEM; | ||
| 50 | debugfs_remove(failslab.ignore_gfp_wait_file); | ||
| 51 | cleanup_fault_attr_dentries(&failslab.attr); | ||
| 52 | } | ||
| 53 | |||
| 54 | return err; | ||
| 55 | } | ||
| 56 | |||
| 57 | late_initcall(failslab_debugfs_init); | ||
| 58 | |||
| 59 | #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ | ||
| @@ -3106,79 +3106,14 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, | |||
| 3106 | #define cache_alloc_debugcheck_after(a,b,objp,d) (objp) | 3106 | #define cache_alloc_debugcheck_after(a,b,objp,d) (objp) |
| 3107 | #endif | 3107 | #endif |
| 3108 | 3108 | ||
| 3109 | #ifdef CONFIG_FAILSLAB | 3109 | static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags) |
| 3110 | |||
| 3111 | static struct failslab_attr { | ||
| 3112 | |||
| 3113 | struct fault_attr attr; | ||
| 3114 | |||
| 3115 | u32 ignore_gfp_wait; | ||
| 3116 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||
| 3117 | struct dentry *ignore_gfp_wait_file; | ||
| 3118 | #endif | ||
| 3119 | |||
| 3120 | } failslab = { | ||
| 3121 | .attr = FAULT_ATTR_INITIALIZER, | ||
| 3122 | .ignore_gfp_wait = 1, | ||
| 3123 | }; | ||
| 3124 | |||
| 3125 | static int __init setup_failslab(char *str) | ||
| 3126 | { | ||
| 3127 | return setup_fault_attr(&failslab.attr, str); | ||
| 3128 | } | ||
| 3129 | __setup("failslab=", setup_failslab); | ||
| 3130 | |||
| 3131 | static int should_failslab(struct kmem_cache *cachep, gfp_t flags) | ||
| 3132 | { | 3110 | { |
| 3133 | if (cachep == &cache_cache) | 3111 | if (cachep == &cache_cache) |
| 3134 | return 0; | 3112 | return false; |
| 3135 | if (flags & __GFP_NOFAIL) | ||
| 3136 | return 0; | ||
| 3137 | if (failslab.ignore_gfp_wait && (flags & __GFP_WAIT)) | ||
| 3138 | return 0; | ||
| 3139 | 3113 | ||
| 3140 | return should_fail(&failslab.attr, obj_size(cachep)); | 3114 | return should_failslab(obj_size(cachep), flags); |
| 3141 | } | 3115 | } |
| 3142 | 3116 | ||
| 3143 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | ||
| 3144 | |||
| 3145 | static int __init failslab_debugfs(void) | ||
| 3146 | { | ||
| 3147 | mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; | ||
| 3148 | struct dentry *dir; | ||
| 3149 | int err; | ||
| 3150 | |||
| 3151 | err = init_fault_attr_dentries(&failslab.attr, "failslab"); | ||
| 3152 | if (err) | ||
| 3153 | return err; | ||
| 3154 | dir = failslab.attr.dentries.dir; | ||
| 3155 | |||
| 3156 | failslab.ignore_gfp_wait_file = | ||
| 3157 | debugfs_create_bool("ignore-gfp-wait", mode, dir, | ||
| 3158 | &failslab.ignore_gfp_wait); | ||
| 3159 | |||
| 3160 | if (!failslab.ignore_gfp_wait_file) { | ||
| 3161 | err = -ENOMEM; | ||
| 3162 | debugfs_remove(failslab.ignore_gfp_wait_file); | ||
| 3163 | cleanup_fault_attr_dentries(&failslab.attr); | ||
| 3164 | } | ||
| 3165 | |||
| 3166 | return err; | ||
| 3167 | } | ||
| 3168 | |||
| 3169 | late_initcall(failslab_debugfs); | ||
| 3170 | |||
| 3171 | #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ | ||
| 3172 | |||
| 3173 | #else /* CONFIG_FAILSLAB */ | ||
| 3174 | |||
| 3175 | static inline int should_failslab(struct kmem_cache *cachep, gfp_t flags) | ||
| 3176 | { | ||
| 3177 | return 0; | ||
| 3178 | } | ||
| 3179 | |||
| 3180 | #endif /* CONFIG_FAILSLAB */ | ||
| 3181 | |||
| 3182 | static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) | 3117 | static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) |
| 3183 | { | 3118 | { |
| 3184 | void *objp; | 3119 | void *objp; |
| @@ -3381,7 +3316,7 @@ __cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, | |||
| 3381 | unsigned long save_flags; | 3316 | unsigned long save_flags; |
| 3382 | void *ptr; | 3317 | void *ptr; |
| 3383 | 3318 | ||
| 3384 | if (should_failslab(cachep, flags)) | 3319 | if (slab_should_failslab(cachep, flags)) |
| 3385 | return NULL; | 3320 | return NULL; |
| 3386 | 3321 | ||
| 3387 | cache_alloc_debugcheck_before(cachep, flags); | 3322 | cache_alloc_debugcheck_before(cachep, flags); |
| @@ -3457,7 +3392,7 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller) | |||
| 3457 | unsigned long save_flags; | 3392 | unsigned long save_flags; |
| 3458 | void *objp; | 3393 | void *objp; |
| 3459 | 3394 | ||
| 3460 | if (should_failslab(cachep, flags)) | 3395 | if (slab_should_failslab(cachep, flags)) |
| 3461 | return NULL; | 3396 | return NULL; |
| 3462 | 3397 | ||
| 3463 | cache_alloc_debugcheck_before(cachep, flags); | 3398 | cache_alloc_debugcheck_before(cachep, flags); |
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/kallsyms.h> | 24 | #include <linux/kallsyms.h> |
| 25 | #include <linux/memory.h> | 25 | #include <linux/memory.h> |
| 26 | #include <linux/math64.h> | 26 | #include <linux/math64.h> |
| 27 | #include <linux/fault-inject.h> | ||
| 27 | 28 | ||
| 28 | /* | 29 | /* |
| 29 | * Lock order: | 30 | * Lock order: |
| @@ -1591,6 +1592,9 @@ static __always_inline void *slab_alloc(struct kmem_cache *s, | |||
| 1591 | unsigned long flags; | 1592 | unsigned long flags; |
| 1592 | unsigned int objsize; | 1593 | unsigned int objsize; |
| 1593 | 1594 | ||
| 1595 | if (should_failslab(s->objsize, gfpflags)) | ||
| 1596 | return NULL; | ||
| 1597 | |||
| 1594 | local_irq_save(flags); | 1598 | local_irq_save(flags); |
| 1595 | c = get_cpu_slab(s, smp_processor_id()); | 1599 | c = get_cpu_slab(s, smp_processor_id()); |
| 1596 | objsize = c->objsize; | 1600 | objsize = c->objsize; |
