diff options
Diffstat (limited to 'mm/slub.c')
-rw-r--r-- | mm/slub.c | 94 |
1 files changed, 59 insertions, 35 deletions
@@ -160,7 +160,7 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s) | |||
160 | */ | 160 | */ |
161 | #define MAX_PARTIAL 10 | 161 | #define MAX_PARTIAL 10 |
162 | 162 | ||
163 | #define DEBUG_DEFAULT_FLAGS (SLAB_DEBUG_FREE | SLAB_RED_ZONE | \ | 163 | #define DEBUG_DEFAULT_FLAGS (SLAB_CONSISTENCY_CHECKS | SLAB_RED_ZONE | \ |
164 | SLAB_POISON | SLAB_STORE_USER) | 164 | SLAB_POISON | SLAB_STORE_USER) |
165 | 165 | ||
166 | /* | 166 | /* |
@@ -1007,20 +1007,32 @@ static void setup_object_debug(struct kmem_cache *s, struct page *page, | |||
1007 | init_tracking(s, object); | 1007 | init_tracking(s, object); |
1008 | } | 1008 | } |
1009 | 1009 | ||
1010 | static noinline int alloc_debug_processing(struct kmem_cache *s, | 1010 | static inline int alloc_consistency_checks(struct kmem_cache *s, |
1011 | struct page *page, | 1011 | struct page *page, |
1012 | void *object, unsigned long addr) | 1012 | void *object, unsigned long addr) |
1013 | { | 1013 | { |
1014 | if (!check_slab(s, page)) | 1014 | if (!check_slab(s, page)) |
1015 | goto bad; | 1015 | return 0; |
1016 | 1016 | ||
1017 | if (!check_valid_pointer(s, page, object)) { | 1017 | if (!check_valid_pointer(s, page, object)) { |
1018 | object_err(s, page, object, "Freelist Pointer check fails"); | 1018 | object_err(s, page, object, "Freelist Pointer check fails"); |
1019 | goto bad; | 1019 | return 0; |
1020 | } | 1020 | } |
1021 | 1021 | ||
1022 | if (!check_object(s, page, object, SLUB_RED_INACTIVE)) | 1022 | if (!check_object(s, page, object, SLUB_RED_INACTIVE)) |
1023 | goto bad; | 1023 | return 0; |
1024 | |||
1025 | return 1; | ||
1026 | } | ||
1027 | |||
1028 | static noinline int alloc_debug_processing(struct kmem_cache *s, | ||
1029 | struct page *page, | ||
1030 | void *object, unsigned long addr) | ||
1031 | { | ||
1032 | if (s->flags & SLAB_CONSISTENCY_CHECKS) { | ||
1033 | if (!alloc_consistency_checks(s, page, object, addr)) | ||
1034 | goto bad; | ||
1035 | } | ||
1024 | 1036 | ||
1025 | /* Success perform special debug activities for allocs */ | 1037 | /* Success perform special debug activities for allocs */ |
1026 | if (s->flags & SLAB_STORE_USER) | 1038 | if (s->flags & SLAB_STORE_USER) |
@@ -1043,39 +1055,21 @@ bad: | |||
1043 | return 0; | 1055 | return 0; |
1044 | } | 1056 | } |
1045 | 1057 | ||
1046 | /* Supports checking bulk free of a constructed freelist */ | 1058 | static inline int free_consistency_checks(struct kmem_cache *s, |
1047 | static noinline int free_debug_processing( | 1059 | struct page *page, void *object, unsigned long addr) |
1048 | struct kmem_cache *s, struct page *page, | ||
1049 | void *head, void *tail, int bulk_cnt, | ||
1050 | unsigned long addr) | ||
1051 | { | 1060 | { |
1052 | struct kmem_cache_node *n = get_node(s, page_to_nid(page)); | ||
1053 | void *object = head; | ||
1054 | int cnt = 0; | ||
1055 | unsigned long uninitialized_var(flags); | ||
1056 | int ret = 0; | ||
1057 | |||
1058 | spin_lock_irqsave(&n->list_lock, flags); | ||
1059 | slab_lock(page); | ||
1060 | |||
1061 | if (!check_slab(s, page)) | ||
1062 | goto out; | ||
1063 | |||
1064 | next_object: | ||
1065 | cnt++; | ||
1066 | |||
1067 | if (!check_valid_pointer(s, page, object)) { | 1061 | if (!check_valid_pointer(s, page, object)) { |
1068 | slab_err(s, page, "Invalid object pointer 0x%p", object); | 1062 | slab_err(s, page, "Invalid object pointer 0x%p", object); |
1069 | goto out; | 1063 | return 0; |
1070 | } | 1064 | } |
1071 | 1065 | ||
1072 | if (on_freelist(s, page, object)) { | 1066 | if (on_freelist(s, page, object)) { |
1073 | object_err(s, page, object, "Object already free"); | 1067 | object_err(s, page, object, "Object already free"); |
1074 | goto out; | 1068 | return 0; |
1075 | } | 1069 | } |
1076 | 1070 | ||
1077 | if (!check_object(s, page, object, SLUB_RED_ACTIVE)) | 1071 | if (!check_object(s, page, object, SLUB_RED_ACTIVE)) |
1078 | goto out; | 1072 | return 0; |
1079 | 1073 | ||
1080 | if (unlikely(s != page->slab_cache)) { | 1074 | if (unlikely(s != page->slab_cache)) { |
1081 | if (!PageSlab(page)) { | 1075 | if (!PageSlab(page)) { |
@@ -1088,7 +1082,37 @@ next_object: | |||
1088 | } else | 1082 | } else |
1089 | object_err(s, page, object, | 1083 | object_err(s, page, object, |
1090 | "page slab pointer corrupt."); | 1084 | "page slab pointer corrupt."); |
1091 | goto out; | 1085 | return 0; |
1086 | } | ||
1087 | return 1; | ||
1088 | } | ||
1089 | |||
1090 | /* Supports checking bulk free of a constructed freelist */ | ||
1091 | static noinline int free_debug_processing( | ||
1092 | struct kmem_cache *s, struct page *page, | ||
1093 | void *head, void *tail, int bulk_cnt, | ||
1094 | unsigned long addr) | ||
1095 | { | ||
1096 | struct kmem_cache_node *n = get_node(s, page_to_nid(page)); | ||
1097 | void *object = head; | ||
1098 | int cnt = 0; | ||
1099 | unsigned long uninitialized_var(flags); | ||
1100 | int ret = 0; | ||
1101 | |||
1102 | spin_lock_irqsave(&n->list_lock, flags); | ||
1103 | slab_lock(page); | ||
1104 | |||
1105 | if (s->flags & SLAB_CONSISTENCY_CHECKS) { | ||
1106 | if (!check_slab(s, page)) | ||
1107 | goto out; | ||
1108 | } | ||
1109 | |||
1110 | next_object: | ||
1111 | cnt++; | ||
1112 | |||
1113 | if (s->flags & SLAB_CONSISTENCY_CHECKS) { | ||
1114 | if (!free_consistency_checks(s, page, object, addr)) | ||
1115 | goto out; | ||
1092 | } | 1116 | } |
1093 | 1117 | ||
1094 | if (s->flags & SLAB_STORE_USER) | 1118 | if (s->flags & SLAB_STORE_USER) |
@@ -1145,7 +1169,7 @@ static int __init setup_slub_debug(char *str) | |||
1145 | for (; *str && *str != ','; str++) { | 1169 | for (; *str && *str != ','; str++) { |
1146 | switch (tolower(*str)) { | 1170 | switch (tolower(*str)) { |
1147 | case 'f': | 1171 | case 'f': |
1148 | slub_debug |= SLAB_DEBUG_FREE; | 1172 | slub_debug |= SLAB_CONSISTENCY_CHECKS; |
1149 | break; | 1173 | break; |
1150 | case 'z': | 1174 | case 'z': |
1151 | slub_debug |= SLAB_RED_ZONE; | 1175 | slub_debug |= SLAB_RED_ZONE; |
@@ -1449,7 +1473,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) | |||
1449 | int order = compound_order(page); | 1473 | int order = compound_order(page); |
1450 | int pages = 1 << order; | 1474 | int pages = 1 << order; |
1451 | 1475 | ||
1452 | if (kmem_cache_debug(s)) { | 1476 | if (s->flags & SLAB_CONSISTENCY_CHECKS) { |
1453 | void *p; | 1477 | void *p; |
1454 | 1478 | ||
1455 | slab_pad_check(s, page); | 1479 | slab_pad_check(s, page); |
@@ -4769,16 +4793,16 @@ SLAB_ATTR_RO(total_objects); | |||
4769 | 4793 | ||
4770 | static ssize_t sanity_checks_show(struct kmem_cache *s, char *buf) | 4794 | static ssize_t sanity_checks_show(struct kmem_cache *s, char *buf) |
4771 | { | 4795 | { |
4772 | return sprintf(buf, "%d\n", !!(s->flags & SLAB_DEBUG_FREE)); | 4796 | return sprintf(buf, "%d\n", !!(s->flags & SLAB_CONSISTENCY_CHECKS)); |
4773 | } | 4797 | } |
4774 | 4798 | ||
4775 | static ssize_t sanity_checks_store(struct kmem_cache *s, | 4799 | static ssize_t sanity_checks_store(struct kmem_cache *s, |
4776 | const char *buf, size_t length) | 4800 | const char *buf, size_t length) |
4777 | { | 4801 | { |
4778 | s->flags &= ~SLAB_DEBUG_FREE; | 4802 | s->flags &= ~SLAB_CONSISTENCY_CHECKS; |
4779 | if (buf[0] == '1') { | 4803 | if (buf[0] == '1') { |
4780 | s->flags &= ~__CMPXCHG_DOUBLE; | 4804 | s->flags &= ~__CMPXCHG_DOUBLE; |
4781 | s->flags |= SLAB_DEBUG_FREE; | 4805 | s->flags |= SLAB_CONSISTENCY_CHECKS; |
4782 | } | 4806 | } |
4783 | return length; | 4807 | return length; |
4784 | } | 4808 | } |
@@ -5313,7 +5337,7 @@ static char *create_unique_id(struct kmem_cache *s) | |||
5313 | *p++ = 'd'; | 5337 | *p++ = 'd'; |
5314 | if (s->flags & SLAB_RECLAIM_ACCOUNT) | 5338 | if (s->flags & SLAB_RECLAIM_ACCOUNT) |
5315 | *p++ = 'a'; | 5339 | *p++ = 'a'; |
5316 | if (s->flags & SLAB_DEBUG_FREE) | 5340 | if (s->flags & SLAB_CONSISTENCY_CHECKS) |
5317 | *p++ = 'F'; | 5341 | *p++ = 'F'; |
5318 | if (!(s->flags & SLAB_NOTRACK)) | 5342 | if (!(s->flags & SLAB_NOTRACK)) |
5319 | *p++ = 't'; | 5343 | *p++ = 't'; |