diff options
| -rw-r--r-- | Documentation/vm/slub.txt | 1 | ||||
| -rw-r--r-- | include/linux/fault-inject.h | 5 | ||||
| -rw-r--r-- | include/linux/slab.h | 5 | ||||
| -rw-r--r-- | mm/failslab.c | 18 | ||||
| -rw-r--r-- | mm/slab.c | 2 | ||||
| -rw-r--r-- | mm/slub.c | 29 |
6 files changed, 52 insertions, 8 deletions
diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt index b37300edf27c..07375e73981a 100644 --- a/Documentation/vm/slub.txt +++ b/Documentation/vm/slub.txt | |||
| @@ -41,6 +41,7 @@ Possible debug options are | |||
| 41 | P Poisoning (object and padding) | 41 | P Poisoning (object and padding) |
| 42 | U User tracking (free and alloc) | 42 | U User tracking (free and alloc) |
| 43 | T Trace (please only use on single slabs) | 43 | T Trace (please only use on single slabs) |
| 44 | A Toggle failslab filter mark for the cache | ||
| 44 | O Switch debugging off for caches that would have | 45 | O Switch debugging off for caches that would have |
| 45 | caused higher minimum slab orders | 46 | caused higher minimum slab orders |
| 46 | - Switch all debugging off (useful if the kernel is | 47 | - Switch all debugging off (useful if the kernel is |
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 06ca9b21dad2..7b64ad40e4ce 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h | |||
| @@ -82,9 +82,10 @@ static inline void cleanup_fault_attr_dentries(struct fault_attr *attr) | |||
| 82 | #endif /* CONFIG_FAULT_INJECTION */ | 82 | #endif /* CONFIG_FAULT_INJECTION */ |
| 83 | 83 | ||
| 84 | #ifdef CONFIG_FAILSLAB | 84 | #ifdef CONFIG_FAILSLAB |
| 85 | extern bool should_failslab(size_t size, gfp_t gfpflags); | 85 | extern bool should_failslab(size_t size, gfp_t gfpflags, unsigned long flags); |
| 86 | #else | 86 | #else |
| 87 | static inline bool should_failslab(size_t size, gfp_t gfpflags) | 87 | static inline bool should_failslab(size_t size, gfp_t gfpflags, |
| 88 | unsigned long flags) | ||
| 88 | { | 89 | { |
| 89 | return false; | 90 | return false; |
| 90 | } | 91 | } |
diff --git a/include/linux/slab.h b/include/linux/slab.h index 2da8372519f5..488446289cab 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h | |||
| @@ -70,6 +70,11 @@ | |||
| 70 | #else | 70 | #else |
| 71 | # define SLAB_NOTRACK 0x00000000UL | 71 | # define SLAB_NOTRACK 0x00000000UL |
| 72 | #endif | 72 | #endif |
| 73 | #ifdef CONFIG_FAILSLAB | ||
| 74 | # define SLAB_FAILSLAB 0x02000000UL /* Fault injection mark */ | ||
| 75 | #else | ||
| 76 | # define SLAB_FAILSLAB 0x00000000UL | ||
| 77 | #endif | ||
| 73 | 78 | ||
| 74 | /* The following flags affect the page allocator grouping pages by mobility */ | 79 | /* The following flags affect the page allocator grouping pages by mobility */ |
| 75 | #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ | 80 | #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ |
diff --git a/mm/failslab.c b/mm/failslab.c index 9339de5f0a91..bb41f98dd8b7 100644 --- a/mm/failslab.c +++ b/mm/failslab.c | |||
| @@ -1,18 +1,22 @@ | |||
| 1 | #include <linux/fault-inject.h> | 1 | #include <linux/fault-inject.h> |
| 2 | #include <linux/gfp.h> | 2 | #include <linux/gfp.h> |
| 3 | #include <linux/slab.h> | ||
| 3 | 4 | ||
| 4 | static struct { | 5 | static struct { |
| 5 | struct fault_attr attr; | 6 | struct fault_attr attr; |
| 6 | u32 ignore_gfp_wait; | 7 | u32 ignore_gfp_wait; |
| 8 | int cache_filter; | ||
| 7 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | 9 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS |
| 8 | struct dentry *ignore_gfp_wait_file; | 10 | struct dentry *ignore_gfp_wait_file; |
| 11 | struct dentry *cache_filter_file; | ||
| 9 | #endif | 12 | #endif |
| 10 | } failslab = { | 13 | } failslab = { |
| 11 | .attr = FAULT_ATTR_INITIALIZER, | 14 | .attr = FAULT_ATTR_INITIALIZER, |
| 12 | .ignore_gfp_wait = 1, | 15 | .ignore_gfp_wait = 1, |
| 16 | .cache_filter = 0, | ||
| 13 | }; | 17 | }; |
| 14 | 18 | ||
| 15 | bool should_failslab(size_t size, gfp_t gfpflags) | 19 | bool should_failslab(size_t size, gfp_t gfpflags, unsigned long cache_flags) |
| 16 | { | 20 | { |
| 17 | if (gfpflags & __GFP_NOFAIL) | 21 | if (gfpflags & __GFP_NOFAIL) |
| 18 | return false; | 22 | return false; |
| @@ -20,6 +24,9 @@ bool should_failslab(size_t size, gfp_t gfpflags) | |||
| 20 | if (failslab.ignore_gfp_wait && (gfpflags & __GFP_WAIT)) | 24 | if (failslab.ignore_gfp_wait && (gfpflags & __GFP_WAIT)) |
| 21 | return false; | 25 | return false; |
| 22 | 26 | ||
| 27 | if (failslab.cache_filter && !(cache_flags & SLAB_FAILSLAB)) | ||
| 28 | return false; | ||
| 29 | |||
| 23 | return should_fail(&failslab.attr, size); | 30 | return should_fail(&failslab.attr, size); |
| 24 | } | 31 | } |
| 25 | 32 | ||
| @@ -30,7 +37,6 @@ static int __init setup_failslab(char *str) | |||
| 30 | __setup("failslab=", setup_failslab); | 37 | __setup("failslab=", setup_failslab); |
| 31 | 38 | ||
| 32 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS | 39 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS |
| 33 | |||
| 34 | static int __init failslab_debugfs_init(void) | 40 | static int __init failslab_debugfs_init(void) |
| 35 | { | 41 | { |
| 36 | mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; | 42 | mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; |
| @@ -46,8 +52,14 @@ static int __init failslab_debugfs_init(void) | |||
| 46 | debugfs_create_bool("ignore-gfp-wait", mode, dir, | 52 | debugfs_create_bool("ignore-gfp-wait", mode, dir, |
| 47 | &failslab.ignore_gfp_wait); | 53 | &failslab.ignore_gfp_wait); |
| 48 | 54 | ||
| 49 | if (!failslab.ignore_gfp_wait_file) { | 55 | failslab.cache_filter_file = |
| 56 | debugfs_create_bool("cache-filter", mode, dir, | ||
| 57 | &failslab.cache_filter); | ||
| 58 | |||
| 59 | if (!failslab.ignore_gfp_wait_file || | ||
| 60 | !failslab.cache_filter_file) { | ||
| 50 | err = -ENOMEM; | 61 | err = -ENOMEM; |
| 62 | debugfs_remove(failslab.cache_filter_file); | ||
| 51 | debugfs_remove(failslab.ignore_gfp_wait_file); | 63 | debugfs_remove(failslab.ignore_gfp_wait_file); |
| 52 | cleanup_fault_attr_dentries(&failslab.attr); | 64 | cleanup_fault_attr_dentries(&failslab.attr); |
| 53 | } | 65 | } |
| @@ -3101,7 +3101,7 @@ static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags) | |||
| 3101 | if (cachep == &cache_cache) | 3101 | if (cachep == &cache_cache) |
| 3102 | return false; | 3102 | return false; |
| 3103 | 3103 | ||
| 3104 | return should_failslab(obj_size(cachep), flags); | 3104 | return should_failslab(obj_size(cachep), flags, cachep->flags); |
| 3105 | } | 3105 | } |
| 3106 | 3106 | ||
| 3107 | static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) | 3107 | static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) |
| @@ -151,7 +151,8 @@ | |||
| 151 | * Set of flags that will prevent slab merging | 151 | * Set of flags that will prevent slab merging |
| 152 | */ | 152 | */ |
| 153 | #define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \ | 153 | #define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \ |
| 154 | SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE) | 154 | SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \ |
| 155 | SLAB_FAILSLAB) | ||
| 155 | 156 | ||
| 156 | #define SLUB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \ | 157 | #define SLUB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \ |
| 157 | SLAB_CACHE_DMA | SLAB_NOTRACK) | 158 | SLAB_CACHE_DMA | SLAB_NOTRACK) |
| @@ -1020,6 +1021,9 @@ static int __init setup_slub_debug(char *str) | |||
| 1020 | case 't': | 1021 | case 't': |
| 1021 | slub_debug |= SLAB_TRACE; | 1022 | slub_debug |= SLAB_TRACE; |
| 1022 | break; | 1023 | break; |
| 1024 | case 'a': | ||
| 1025 | slub_debug |= SLAB_FAILSLAB; | ||
| 1026 | break; | ||
| 1023 | default: | 1027 | default: |
| 1024 | printk(KERN_ERR "slub_debug option '%c' " | 1028 | printk(KERN_ERR "slub_debug option '%c' " |
| 1025 | "unknown. skipped\n", *str); | 1029 | "unknown. skipped\n", *str); |
| @@ -1718,7 +1722,7 @@ static __always_inline void *slab_alloc(struct kmem_cache *s, | |||
| 1718 | lockdep_trace_alloc(gfpflags); | 1722 | lockdep_trace_alloc(gfpflags); |
| 1719 | might_sleep_if(gfpflags & __GFP_WAIT); | 1723 | might_sleep_if(gfpflags & __GFP_WAIT); |
| 1720 | 1724 | ||
| 1721 | if (should_failslab(s->objsize, gfpflags)) | 1725 | if (should_failslab(s->objsize, gfpflags, s->flags)) |
| 1722 | return NULL; | 1726 | return NULL; |
| 1723 | 1727 | ||
| 1724 | local_irq_save(flags); | 1728 | local_irq_save(flags); |
| @@ -4171,6 +4175,23 @@ static ssize_t trace_store(struct kmem_cache *s, const char *buf, | |||
| 4171 | } | 4175 | } |
| 4172 | SLAB_ATTR(trace); | 4176 | SLAB_ATTR(trace); |
| 4173 | 4177 | ||
| 4178 | #ifdef CONFIG_FAILSLAB | ||
| 4179 | static ssize_t failslab_show(struct kmem_cache *s, char *buf) | ||
| 4180 | { | ||
| 4181 | return sprintf(buf, "%d\n", !!(s->flags & SLAB_FAILSLAB)); | ||
| 4182 | } | ||
| 4183 | |||
| 4184 | static ssize_t failslab_store(struct kmem_cache *s, const char *buf, | ||
| 4185 | size_t length) | ||
| 4186 | { | ||
| 4187 | s->flags &= ~SLAB_FAILSLAB; | ||
| 4188 | if (buf[0] == '1') | ||
| 4189 | s->flags |= SLAB_FAILSLAB; | ||
| 4190 | return length; | ||
| 4191 | } | ||
| 4192 | SLAB_ATTR(failslab); | ||
| 4193 | #endif | ||
| 4194 | |||
| 4174 | static ssize_t reclaim_account_show(struct kmem_cache *s, char *buf) | 4195 | static ssize_t reclaim_account_show(struct kmem_cache *s, char *buf) |
| 4175 | { | 4196 | { |
| 4176 | return sprintf(buf, "%d\n", !!(s->flags & SLAB_RECLAIM_ACCOUNT)); | 4197 | return sprintf(buf, "%d\n", !!(s->flags & SLAB_RECLAIM_ACCOUNT)); |
| @@ -4467,6 +4488,10 @@ static struct attribute *slab_attrs[] = { | |||
| 4467 | &deactivate_remote_frees_attr.attr, | 4488 | &deactivate_remote_frees_attr.attr, |
| 4468 | &order_fallback_attr.attr, | 4489 | &order_fallback_attr.attr, |
| 4469 | #endif | 4490 | #endif |
| 4491 | #ifdef CONFIG_FAILSLAB | ||
| 4492 | &failslab_attr.attr, | ||
| 4493 | #endif | ||
| 4494 | |||
| 4470 | NULL | 4495 | NULL |
| 4471 | }; | 4496 | }; |
| 4472 | 4497 | ||
