]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - mm/slub.c
ARM: dts: imx6ul: add support for Ka-Ro electronics TXUL mainboard
[karo-tx-linux.git] / mm / slub.c
index 2e1355ac056b02a51778b2b1eef770b276626309..0f7e9e94e57c814c1415bf06e7ab0b4648d1ad67 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
 
 #include "internal.h"
 
+#ifdef CONFIG_KASAN
+#include "kasan/kasan.h"
+#endif
+
 /*
  * Lock order:
  *   1. slab_mutex (Global Mutex)
@@ -124,6 +128,14 @@ static inline int kmem_cache_debug(struct kmem_cache *s)
 #endif
 }
 
+static inline void *fixup_red_left(struct kmem_cache *s, void *p)
+{
+       if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE)
+               p += s->red_left_pad;
+
+       return p;
+}
+
 static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
 {
 #ifdef CONFIG_SLUB_CPU_PARTIAL
@@ -224,24 +236,6 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si)
  *                     Core slab cache functions
  *******************************************************************/
 
-/* Verify that a pointer has an address that is valid within a slab page */
-static inline int check_valid_pointer(struct kmem_cache *s,
-                               struct page *page, const void *object)
-{
-       void *base;
-
-       if (!object)
-               return 1;
-
-       base = page_address(page);
-       if (object < base || object >= base + page->objects * s->size ||
-               (object - base) % s->size) {
-               return 0;
-       }
-
-       return 1;
-}
-
 static inline void *get_freepointer(struct kmem_cache *s, void *object)
 {
        return *(void **)(object + s->offset);
@@ -256,11 +250,9 @@ static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
        void *p;
 
-#ifdef CONFIG_DEBUG_PAGEALLOC
+       if (!debug_pagealloc_enabled())
+               return get_freepointer(s, object);
        probe_kernel_read(&p, (void **)(object + s->offset), sizeof(p));
-#else
-       p = get_freepointer(s, object);
-#endif
        return p;
 }
 
@@ -284,30 +276,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;
@@ -458,6 +426,22 @@ static void get_map(struct kmem_cache *s, struct page *page, unsigned long *map)
                set_bit(slab_index(p, s, addr), map);
 }
 
+static inline int size_from_object(struct kmem_cache *s)
+{
+       if (s->flags & SLAB_RED_ZONE)
+               return s->size - s->red_left_pad;
+
+       return s->size;
+}
+
+static inline void *restore_red_left(struct kmem_cache *s, void *p)
+{
+       if (s->flags & SLAB_RED_ZONE)
+               p -= s->red_left_pad;
+
+       return p;
+}
+
 /*
  * Debug settings:
  */
@@ -491,6 +475,26 @@ static inline void metadata_access_disable(void)
 /*
  * Object debugging
  */
+
+/* Verify that a pointer has an address that is valid within a slab page */
+static inline int check_valid_pointer(struct kmem_cache *s,
+                               struct page *page, void *object)
+{
+       void *base;
+
+       if (!object)
+               return 1;
+
+       base = page_address(page);
+       object = restore_red_left(s, object);
+       if (object < base || object >= base + page->objects * s->size ||
+               (object - base) % s->size) {
+               return 0;
+       }
+
+       return 1;
+}
+
 static void print_section(char *text, u8 *addr, unsigned int length)
 {
        metadata_access_enable();
@@ -630,7 +634,9 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p)
        pr_err("INFO: Object 0x%p @offset=%tu fp=0x%p\n\n",
               p, p - addr, get_freepointer(s, p));
 
-       if (p > addr + 16)
+       if (s->flags & SLAB_RED_ZONE)
+               print_section("Redzone ", p - s->red_left_pad, s->red_left_pad);
+       else if (p > addr + 16)
                print_section("Bytes b4 ", p - 16, 16);
 
        print_section("Object ", p, min_t(unsigned long, s->object_size,
@@ -647,9 +653,9 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p)
        if (s->flags & SLAB_STORE_USER)
                off += 2 * sizeof(struct track);
 
-       if (off != s->size)
+       if (off != size_from_object(s))
                /* Beginning of the filler is the free pointer */
-               print_section("Padding ", p + off, s->size - off);
+               print_section("Padding ", p + off, size_from_object(s) - off);
 
        dump_stack();
 }
@@ -679,6 +685,9 @@ static void init_object(struct kmem_cache *s, void *object, u8 val)
 {
        u8 *p = object;
 
+       if (s->flags & SLAB_RED_ZONE)
+               memset(p - s->red_left_pad, val, s->red_left_pad);
+
        if (s->flags & __OBJECT_POISON) {
                memset(p, POISON_FREE, s->object_size - 1);
                p[s->object_size - 1] = POISON_END;
@@ -771,11 +780,11 @@ static int check_pad_bytes(struct kmem_cache *s, struct page *page, u8 *p)
                /* We also have user information there */
                off += 2 * sizeof(struct track);
 
-       if (s->size == off)
+       if (size_from_object(s) == off)
                return 1;
 
        return check_bytes_and_report(s, page, p, "Object padding",
-                               p + off, POISON_INUSE, s->size - off);
+                       p + off, POISON_INUSE, size_from_object(s) - off);
 }
 
 /* Check the pad bytes at the end of a slab page */
@@ -819,6 +828,10 @@ static int check_object(struct kmem_cache *s, struct page *page,
        u8 *endobject = object + s->object_size;
 
        if (s->flags & SLAB_RED_ZONE) {
+               if (!check_bytes_and_report(s, page, object, "Redzone",
+                       object - s->red_left_pad, val, s->red_left_pad))
+                       return 0;
+
                if (!check_bytes_and_report(s, page, object, "Redzone",
                        endobject, val, s->inuse - s->object_size))
                        return 0;
@@ -1021,14 +1034,17 @@ static inline void dec_slabs_node(struct kmem_cache *s, int node, int objects)
 }
 
 /* Object debug checks for alloc/free paths */
-static void setup_object_debug(struct kmem_cache *s, struct page *page,
+static void *setup_object_debug(struct kmem_cache *s, struct page *page,
                                                                void *object)
 {
        if (!(s->flags & (SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON)))
-               return;
+               return object;
 
+       object = fixup_red_left(s, object);
        init_object(s, object, SLUB_RED_INACTIVE);
        init_tracking(s, object);
+
+       return object;
 }
 
 static noinline int alloc_debug_processing(struct kmem_cache *s,
@@ -1225,8 +1241,8 @@ unsigned long kmem_cache_flags(unsigned long object_size,
        return flags;
 }
 #else /* !CONFIG_SLUB_DEBUG */
-static inline void setup_object_debug(struct kmem_cache *s,
-                       struct page *page, void *object) {}
+static inline void *setup_object_debug(struct kmem_cache *s,
+                       struct page *page, void *object) { return object; }
 
 static inline int alloc_debug_processing(struct kmem_cache *s,
        struct page *page, void *object, unsigned long addr) { return 0; }
@@ -1281,36 +1297,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);
@@ -1359,15 +1345,17 @@ static inline void slab_free_freelist_hook(struct kmem_cache *s,
 #endif
 }
 
-static void setup_object(struct kmem_cache *s, struct page *page,
+static void *setup_object(struct kmem_cache *s, struct page *page,
                                void *object)
 {
-       setup_object_debug(s, page, object);
+       object = setup_object_debug(s, page, object);
        if (unlikely(s->ctor)) {
                kasan_unpoison_object_data(s, object);
                s->ctor(object);
                kasan_poison_object_data(s, object);
        }
+
+       return object;
 }
 
 /*
@@ -1463,14 +1451,16 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
        kasan_poison_slab(page);
 
        for_each_object_idx(p, idx, s, start, page->objects) {
-               setup_object(s, page, p);
-               if (likely(idx < page->objects))
-                       set_freepointer(s, p, p + s->size);
-               else
-                       set_freepointer(s, p, NULL);
+               void *object = setup_object(s, page, p);
+
+               if (likely(idx < page->objects)) {
+                       set_freepointer(s, object,
+                               fixup_red_left(s, p + s->size));
+               } else
+                       set_freepointer(s, object, NULL);
        }
 
-       page->freelist = start;
+       page->freelist = fixup_red_left(s, start);
        page->inuse = page->objects;
        page->frozen = 1;
 
@@ -1511,8 +1501,11 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
 
                slab_pad_check(s, page);
                for_each_object(p, s, page_address(page),
-                                               page->objects)
-                       check_object(s, page, p, SLUB_RED_INACTIVE);
+                                               page->objects) {
+                       void *object = fixup_red_left(s, p);
+
+                       check_object(s, page, object, SLUB_RED_INACTIVE);
+               }
        }
 
        kmemcheck_free_shadow(page, compound_order(page));
@@ -1528,7 +1521,8 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
        page_mapcount_reset(page);
        if (current->reclaim_state)
                current->reclaim_state->reclaimed_slab += pages;
-       __free_kmem_pages(page, order);
+       memcg_uncharge_slab(page, order, s);
+       __free_pages(page, order);
 }
 
 #define need_reserve_slab_rcu                                          \
@@ -1592,18 +1586,12 @@ static inline void add_partial(struct kmem_cache_node *n,
        __add_partial(n, page, tail);
 }
 
-static inline void
-__remove_partial(struct kmem_cache_node *n, struct page *page)
-{
-       list_del(&page->lru);
-       n->nr_partial--;
-}
-
 static inline void remove_partial(struct kmem_cache_node *n,
                                        struct page *page)
 {
        lockdep_assert_held(&n->list_lock);
-       __remove_partial(n, page);
+       list_del(&page->lru);
+       n->nr_partial--;
 }
 
 /*
@@ -2821,6 +2809,7 @@ struct detached_freelist {
        void *tail;
        void *freelist;
        int cnt;
+       struct kmem_cache *s;
 };
 
 /*
@@ -2835,26 +2824,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 */
@@ -2868,7 +2876,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 */
@@ -2887,25 +2895,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);
@@ -3184,6 +3187,12 @@ static void free_kmem_cache_nodes(struct kmem_cache *s)
        }
 }
 
+void __kmem_cache_release(struct kmem_cache *s)
+{
+       free_percpu(s->cpu_slab);
+       free_kmem_cache_nodes(s);
+}
+
 static int init_kmem_cache_nodes(struct kmem_cache *s)
 {
        int node;
@@ -3294,6 +3303,16 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
                 * of the object.
                 */
                size += sizeof(void *);
+
+       if (flags & SLAB_RED_ZONE) {
+               s->red_left_pad = sizeof(void *);
+#ifdef CONFIG_KASAN
+               s->red_left_pad = min_t(int, s->red_left_pad,
+                               KASAN_SHADOW_SCALE_SIZE);
+#endif
+               s->red_left_pad = ALIGN(s->red_left_pad, s->align);
+               size += s->red_left_pad;
+       }
 #endif
 
        /*
@@ -3430,10 +3449,12 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page,
 
        get_map(s, page, map);
        for_each_object(p, s, addr, page->objects) {
+               void *object = fixup_red_left(s, p);
 
                if (!test_bit(slab_index(p, s, addr), map)) {
-                       pr_err("INFO: Object 0x%p @offset=%tu\n", p, p - addr);
-                       print_tracking(s, p);
+                       pr_err("INFO: Object 0x%p @offset=%tu\n",
+                                       object, object - addr);
+                       print_tracking(s, object);
                }
        }
        slab_unlock(page);
@@ -3443,28 +3464,31 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page,
 
 /*
  * Attempt to free all partial slabs on a node.
- * This is called from kmem_cache_close(). We must be the last thread
- * using the cache and therefore we do not need to lock anymore.
+ * This is called from __kmem_cache_shutdown(). We must take list_lock
+ * because sysfs file might still access partial list after the shutdowning.
  */
 static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
 {
        struct page *page, *h;
 
+       BUG_ON(irqs_disabled());
+       spin_lock_irq(&n->list_lock);
        list_for_each_entry_safe(page, h, &n->partial, lru) {
                if (!page->inuse) {
-                       __remove_partial(n, page);
+                       remove_partial(n, page);
                        discard_slab(s, page);
                } else {
                        list_slab_objects(s, page,
-                       "Objects remaining in %s on kmem_cache_close()");
+                       "Objects remaining in %s on __kmem_cache_shutdown()");
                }
        }
+       spin_unlock_irq(&n->list_lock);
 }
 
 /*
  * Release all resources used by a slab cache.
  */
-static inline int kmem_cache_close(struct kmem_cache *s)
+int __kmem_cache_shutdown(struct kmem_cache *s)
 {
        int node;
        struct kmem_cache_node *n;
@@ -3476,16 +3500,9 @@ static inline int kmem_cache_close(struct kmem_cache *s)
                if (n->nr_partial || slabs_node(s, node))
                        return 1;
        }
-       free_percpu(s->cpu_slab);
-       free_kmem_cache_nodes(s);
        return 0;
 }
 
-int __kmem_cache_shutdown(struct kmem_cache *s)
-{
-       return kmem_cache_close(s);
-}
-
 /********************************************************************
  *             Kmalloc subsystem
  *******************************************************************/
@@ -3980,7 +3997,7 @@ int __kmem_cache_create(struct kmem_cache *s, unsigned long flags)
        memcg_propagate_slab_attrs(s);
        err = sysfs_slab_add(s);
        if (err)
-               kmem_cache_close(s);
+               __kmem_cache_release(s);
 
        return err;
 }
@@ -4102,15 +4119,21 @@ static int validate_slab(struct kmem_cache *s, struct page *page,
 
        get_map(s, page, map);
        for_each_object(p, s, addr, page->objects) {
+               void *object = fixup_red_left(s, p);
+
                if (test_bit(slab_index(p, s, addr), map))
-                       if (!check_object(s, page, p, SLUB_RED_INACTIVE))
+                       if (!check_object(s, page, object, SLUB_RED_INACTIVE))
                                return 0;
        }
 
-       for_each_object(p, s, addr, page->objects)
+       for_each_object(p, s, addr, page->objects) {
+               void *object = fixup_red_left(s, p);
+
                if (!test_bit(slab_index(p, s, addr), map))
-                       if (!check_object(s, page, p, SLUB_RED_ACTIVE))
+                       if (!check_object(s, page, object, SLUB_RED_ACTIVE))
                                return 0;
+       }
+
        return 1;
 }
 
@@ -4308,9 +4331,12 @@ static void process_slab(struct loc_track *t, struct kmem_cache *s,
        bitmap_zero(map, page->objects);
        get_map(s, page, map);
 
-       for_each_object(p, s, addr, page->objects)
+       for_each_object(p, s, addr, page->objects) {
+               void *object = fixup_red_left(s, p);
+
                if (!test_bit(slab_index(p, s, addr), map))
-                       add_location(t, s, get_track(s, p, alloc));
+                       add_location(t, s, get_track(s, object, alloc));
+       }
 }
 
 static int list_locations(struct kmem_cache *s, char *buf,