slub: convert SLAB_DEBUG_FREE to SLAB_CONSISTENCY_CHECKS
[deliverable/linux.git] / mm / slub.c
index d8fbd4a6ed599882489143665ca750eb7613fd65..9cde663bbb1096fe544fb710ed3287dc33666e68 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -160,7 +160,7 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
  */
 #define MAX_PARTIAL 10
 
-#define DEBUG_DEFAULT_FLAGS (SLAB_DEBUG_FREE | SLAB_RED_ZONE | \
+#define DEBUG_DEFAULT_FLAGS (SLAB_CONSISTENCY_CHECKS | SLAB_RED_ZONE | \
                                SLAB_POISON | SLAB_STORE_USER)
 
 /*
@@ -284,30 +284,6 @@ static inline int slab_index(void *p, struct kmem_cache *s, void *addr)
        return (p - addr) / s->size;
 }
 
-static inline size_t slab_ksize(const struct kmem_cache *s)
-{
-#ifdef CONFIG_SLUB_DEBUG
-       /*
-        * Debugging requires use of the padding between object
-        * and whatever may come after it.
-        */
-       if (s->flags & (SLAB_RED_ZONE | SLAB_POISON))
-               return s->object_size;
-
-#endif
-       /*
-        * If we have the need to store the freelist pointer
-        * back there or track user information then we can
-        * only use the space before that information.
-        */
-       if (s->flags & (SLAB_DESTROY_BY_RCU | SLAB_STORE_USER))
-               return s->inuse;
-       /*
-        * Else we can use all the padding etc for the allocation
-        */
-       return s->size;
-}
-
 static inline int order_objects(int order, unsigned long size, int reserved)
 {
        return ((PAGE_SIZE << order) - reserved) / size;
@@ -1031,20 +1007,32 @@ static void setup_object_debug(struct kmem_cache *s, struct page *page,
        init_tracking(s, object);
 }
 
-static noinline int alloc_debug_processing(struct kmem_cache *s,
+static inline int alloc_consistency_checks(struct kmem_cache *s,
                                        struct page *page,
                                        void *object, unsigned long addr)
 {
        if (!check_slab(s, page))
-               goto bad;
+               return 0;
 
        if (!check_valid_pointer(s, page, object)) {
                object_err(s, page, object, "Freelist Pointer check fails");
-               goto bad;
+               return 0;
        }
 
        if (!check_object(s, page, object, SLUB_RED_INACTIVE))
-               goto bad;
+               return 0;
+
+       return 1;
+}
+
+static noinline int alloc_debug_processing(struct kmem_cache *s,
+                                       struct page *page,
+                                       void *object, unsigned long addr)
+{
+       if (s->flags & SLAB_CONSISTENCY_CHECKS) {
+               if (!alloc_consistency_checks(s, page, object, addr))
+                       goto bad;
+       }
 
        /* Success perform special debug activities for allocs */
        if (s->flags & SLAB_STORE_USER)
@@ -1067,37 +1055,21 @@ bad:
        return 0;
 }
 
-/* Supports checking bulk free of a constructed freelist */
-static noinline struct kmem_cache_node *free_debug_processing(
-       struct kmem_cache *s, struct page *page,
-       void *head, void *tail, int bulk_cnt,
-       unsigned long addr, unsigned long *flags)
+static inline int free_consistency_checks(struct kmem_cache *s,
+               struct page *page, void *object, unsigned long addr)
 {
-       struct kmem_cache_node *n = get_node(s, page_to_nid(page));
-       void *object = head;
-       int cnt = 0;
-
-       spin_lock_irqsave(&n->list_lock, *flags);
-       slab_lock(page);
-
-       if (!check_slab(s, page))
-               goto fail;
-
-next_object:
-       cnt++;
-
        if (!check_valid_pointer(s, page, object)) {
                slab_err(s, page, "Invalid object pointer 0x%p", object);
-               goto fail;
+               return 0;
        }
 
        if (on_freelist(s, page, object)) {
                object_err(s, page, object, "Object already free");
-               goto fail;
+               return 0;
        }
 
        if (!check_object(s, page, object, SLUB_RED_ACTIVE))
-               goto out;
+               return 0;
 
        if (unlikely(s != page->slab_cache)) {
                if (!PageSlab(page)) {
@@ -1110,7 +1082,37 @@ next_object:
                } else
                        object_err(s, page, object,
                                        "page slab pointer corrupt.");
-               goto fail;
+               return 0;
+       }
+       return 1;
+}
+
+/* Supports checking bulk free of a constructed freelist */
+static noinline int free_debug_processing(
+       struct kmem_cache *s, struct page *page,
+       void *head, void *tail, int bulk_cnt,
+       unsigned long addr)
+{
+       struct kmem_cache_node *n = get_node(s, page_to_nid(page));
+       void *object = head;
+       int cnt = 0;
+       unsigned long uninitialized_var(flags);
+       int ret = 0;
+
+       spin_lock_irqsave(&n->list_lock, flags);
+       slab_lock(page);
+
+       if (s->flags & SLAB_CONSISTENCY_CHECKS) {
+               if (!check_slab(s, page))
+                       goto out;
+       }
+
+next_object:
+       cnt++;
+
+       if (s->flags & SLAB_CONSISTENCY_CHECKS) {
+               if (!free_consistency_checks(s, page, object, addr))
+                       goto out;
        }
 
        if (s->flags & SLAB_STORE_USER)
@@ -1124,23 +1126,18 @@ next_object:
                object = get_freepointer(s, object);
                goto next_object;
        }
+       ret = 1;
+
 out:
        if (cnt != bulk_cnt)
                slab_err(s, page, "Bulk freelist count(%d) invalid(%d)\n",
                         bulk_cnt, cnt);
 
        slab_unlock(page);
-       /*
-        * Keep node_lock to preserve integrity
-        * until the object is actually freed
-        */
-       return n;
-
-fail:
-       slab_unlock(page);
-       spin_unlock_irqrestore(&n->list_lock, *flags);
-       slab_fix(s, "Object at 0x%p not freed", object);
-       return NULL;
+       spin_unlock_irqrestore(&n->list_lock, flags);
+       if (!ret)
+               slab_fix(s, "Object at 0x%p not freed", object);
+       return ret;
 }
 
 static int __init setup_slub_debug(char *str)
@@ -1172,7 +1169,7 @@ static int __init setup_slub_debug(char *str)
        for (; *str && *str != ','; str++) {
                switch (tolower(*str)) {
                case 'f':
-                       slub_debug |= SLAB_DEBUG_FREE;
+                       slub_debug |= SLAB_CONSISTENCY_CHECKS;
                        break;
                case 'z':
                        slub_debug |= SLAB_RED_ZONE;
@@ -1231,10 +1228,10 @@ static inline void setup_object_debug(struct kmem_cache *s,
 static inline int alloc_debug_processing(struct kmem_cache *s,
        struct page *page, void *object, unsigned long addr) { return 0; }
 
-static inline struct kmem_cache_node *free_debug_processing(
+static inline int free_debug_processing(
        struct kmem_cache *s, struct page *page,
        void *head, void *tail, int bulk_cnt,
-       unsigned long addr, unsigned long *flags) { return NULL; }
+       unsigned long addr) { return 0; }
 
 static inline int slab_pad_check(struct kmem_cache *s, struct page *page)
                        { return 1; }
@@ -1281,36 +1278,6 @@ static inline void kfree_hook(const void *x)
        kasan_kfree_large(x);
 }
 
-static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s,
-                                                    gfp_t flags)
-{
-       flags &= gfp_allowed_mask;
-       lockdep_trace_alloc(flags);
-       might_sleep_if(gfpflags_allow_blocking(flags));
-
-       if (should_failslab(s->object_size, flags, s->flags))
-               return NULL;
-
-       return memcg_kmem_get_cache(s, flags);
-}
-
-static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
-                                       size_t size, void **p)
-{
-       size_t i;
-
-       flags &= gfp_allowed_mask;
-       for (i = 0; i < size; i++) {
-               void *object = p[i];
-
-               kmemcheck_slab_alloc(s, flags, object, slab_ksize(s));
-               kmemleak_alloc_recursive(object, s->object_size, 1,
-                                        s->flags, flags);
-               kasan_slab_alloc(s, object);
-       }
-       memcg_kmem_put_cache(s);
-}
-
 static inline void slab_free_hook(struct kmem_cache *s, void *x)
 {
        kmemleak_free_recursive(x, s->flags);
@@ -1506,7 +1473,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
        int order = compound_order(page);
        int pages = 1 << order;
 
-       if (kmem_cache_debug(s)) {
+       if (s->flags & SLAB_CONSISTENCY_CHECKS) {
                void *p;
 
                slab_pad_check(s, page);
@@ -2642,8 +2609,7 @@ static void __slab_free(struct kmem_cache *s, struct page *page,
        stat(s, FREE_SLOWPATH);
 
        if (kmem_cache_debug(s) &&
-           !(n = free_debug_processing(s, page, head, tail, cnt,
-                                       addr, &flags)))
+           !free_debug_processing(s, page, head, tail, cnt, addr))
                return;
 
        do {
@@ -2815,6 +2781,7 @@ struct detached_freelist {
        void *tail;
        void *freelist;
        int cnt;
+       struct kmem_cache *s;
 };
 
 /*
@@ -2829,26 +2796,45 @@ struct detached_freelist {
  * synchronization primitive.  Look ahead in the array is limited due
  * to performance reasons.
  */
-static int build_detached_freelist(struct kmem_cache *s, size_t size,
-                                  void **p, struct detached_freelist *df)
+static inline
+int build_detached_freelist(struct kmem_cache *s, size_t size,
+                           void **p, struct detached_freelist *df)
 {
        size_t first_skipped_index = 0;
        int lookahead = 3;
        void *object;
+       struct page *page;
 
        /* Always re-init detached_freelist */
        df->page = NULL;
 
        do {
                object = p[--size];
+               /* Do we need !ZERO_OR_NULL_PTR(object) here? (for kfree) */
        } while (!object && size);
 
        if (!object)
                return 0;
 
+       page = virt_to_head_page(object);
+       if (!s) {
+               /* Handle kalloc'ed objects */
+               if (unlikely(!PageSlab(page))) {
+                       BUG_ON(!PageCompound(page));
+                       kfree_hook(object);
+                       __free_kmem_pages(page, compound_order(page));
+                       p[size] = NULL; /* mark object processed */
+                       return size;
+               }
+               /* Derive kmem_cache from object */
+               df->s = page->slab_cache;
+       } else {
+               df->s = cache_from_obj(s, object); /* Support for memcg */
+       }
+
        /* Start new detached freelist */
-       set_freepointer(s, object, NULL);
-       df->page = virt_to_head_page(object);
+       df->page = page;
+       set_freepointer(df->s, object, NULL);
        df->tail = object;
        df->freelist = object;
        p[size] = NULL; /* mark object processed */
@@ -2862,7 +2848,7 @@ static int build_detached_freelist(struct kmem_cache *s, size_t size,
                /* df->page is always set at this point */
                if (df->page == virt_to_head_page(object)) {
                        /* Opportunity build freelist */
-                       set_freepointer(s, object, df->freelist);
+                       set_freepointer(df->s, object, df->freelist);
                        df->freelist = object;
                        df->cnt++;
                        p[size] = NULL; /* mark object processed */
@@ -2881,25 +2867,20 @@ static int build_detached_freelist(struct kmem_cache *s, size_t size,
        return first_skipped_index;
 }
 
-
 /* Note that interrupts must be enabled when calling this function. */
-void kmem_cache_free_bulk(struct kmem_cache *orig_s, size_t size, void **p)
+void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
 {
        if (WARN_ON(!size))
                return;
 
        do {
                struct detached_freelist df;
-               struct kmem_cache *s;
-
-               /* Support for memcg */
-               s = cache_from_obj(orig_s, p[size - 1]);
 
                size = build_detached_freelist(s, size, p, &df);
                if (unlikely(!df.page))
                        continue;
 
-               slab_free(s, df.page, df.freelist, df.tail, df.cnt, _RET_IP_);
+               slab_free(df.s, df.page, df.freelist, df.tail, df.cnt,_RET_IP_);
        } while (likely(size));
 }
 EXPORT_SYMBOL(kmem_cache_free_bulk);
@@ -4812,16 +4793,16 @@ SLAB_ATTR_RO(total_objects);
 
 static ssize_t sanity_checks_show(struct kmem_cache *s, char *buf)
 {
-       return sprintf(buf, "%d\n", !!(s->flags & SLAB_DEBUG_FREE));
+       return sprintf(buf, "%d\n", !!(s->flags & SLAB_CONSISTENCY_CHECKS));
 }
 
 static ssize_t sanity_checks_store(struct kmem_cache *s,
                                const char *buf, size_t length)
 {
-       s->flags &= ~SLAB_DEBUG_FREE;
+       s->flags &= ~SLAB_CONSISTENCY_CHECKS;
        if (buf[0] == '1') {
                s->flags &= ~__CMPXCHG_DOUBLE;
-               s->flags |= SLAB_DEBUG_FREE;
+               s->flags |= SLAB_CONSISTENCY_CHECKS;
        }
        return length;
 }
@@ -5356,7 +5337,7 @@ static char *create_unique_id(struct kmem_cache *s)
                *p++ = 'd';
        if (s->flags & SLAB_RECLAIM_ACCOUNT)
                *p++ = 'a';
-       if (s->flags & SLAB_DEBUG_FREE)
+       if (s->flags & SLAB_CONSISTENCY_CHECKS)
                *p++ = 'F';
        if (!(s->flags & SLAB_NOTRACK))
                *p++ = 't';
This page took 0.030951 seconds and 5 git commands to generate.