mm, slub: extract get_partial() from new_slab_objects()
[linux-2.6-microblaze.git] / mm / slub.c
index af984e4..cd6aeee 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -454,6 +454,18 @@ static inline bool cmpxchg_double_slab(struct kmem_cache *s, struct page *page,
 static unsigned long object_map[BITS_TO_LONGS(MAX_OBJS_PER_PAGE)];
 static DEFINE_SPINLOCK(object_map_lock);
 
+static void __fill_map(unsigned long *obj_map, struct kmem_cache *s,
+                      struct page *page)
+{
+       void *addr = page_address(page);
+       void *p;
+
+       bitmap_zero(obj_map, page->objects);
+
+       for (p = page->freelist; p; p = get_freepointer(s, p))
+               set_bit(__obj_to_index(s, addr, p), obj_map);
+}
+
 #if IS_ENABLED(CONFIG_KUNIT)
 static bool slab_add_kunit_errors(void)
 {
@@ -483,17 +495,11 @@ static inline bool slab_add_kunit_errors(void) { return false; }
 static unsigned long *get_map(struct kmem_cache *s, struct page *page)
        __acquires(&object_map_lock)
 {
-       void *p;
-       void *addr = page_address(page);
-
        VM_BUG_ON(!irqs_disabled());
 
        spin_lock(&object_map_lock);
 
-       bitmap_zero(object_map, page->objects);
-
-       for (p = page->freelist; p; p = get_freepointer(s, p))
-               set_bit(__obj_to_index(s, addr, p), object_map);
+       __fill_map(object_map, s, page);
 
        return object_map;
 }
@@ -576,8 +582,8 @@ static void print_section(char *level, char *text, u8 *addr,
                          unsigned int length)
 {
        metadata_access_enable();
-       print_hex_dump(level, kasan_reset_tag(text), DUMP_PREFIX_ADDRESS,
-                       16, 1, addr, length, 1);
+       print_hex_dump(level, text, DUMP_PREFIX_ADDRESS,
+                       16, 1, kasan_reset_tag((void *)addr), length, 1);
        metadata_access_disable();
 }
 
@@ -1400,12 +1406,13 @@ check_slabs:
 static int __init setup_slub_debug(char *str)
 {
        slab_flags_t flags;
+       slab_flags_t global_flags;
        char *saved_str;
        char *slab_list;
        bool global_slub_debug_changed = false;
        bool slab_list_specified = false;
 
-       slub_debug = DEBUG_DEFAULT_FLAGS;
+       global_flags = DEBUG_DEFAULT_FLAGS;
        if (*str++ != '=' || !*str)
                /*
                 * No options specified. Switch on full debugging.
@@ -1417,7 +1424,7 @@ static int __init setup_slub_debug(char *str)
                str = parse_slub_debug_flags(str, &flags, &slab_list, true);
 
                if (!slab_list) {
-                       slub_debug = flags;
+                       global_flags = flags;
                        global_slub_debug_changed = true;
                } else {
                        slab_list_specified = true;
@@ -1426,16 +1433,18 @@ static int __init setup_slub_debug(char *str)
 
        /*
         * For backwards compatibility, a single list of flags with list of
-        * slabs means debugging is only enabled for those slabs, so the global
-        * slub_debug should be 0. We can extended that to multiple lists as
+        * slabs means debugging is only changed for those slabs, so the global
+        * slub_debug should be unchanged (0 or DEBUG_DEFAULT_FLAGS, depending
+        * on CONFIG_SLUB_DEBUG_ON). We can extended that to multiple lists as
         * long as there is no option specifying flags without a slab list.
         */
        if (slab_list_specified) {
                if (!global_slub_debug_changed)
-                       slub_debug = 0;
+                       global_flags = slub_debug;
                slub_debug_string = saved_str;
        }
 out:
+       slub_debug = global_flags;
        if (slub_debug != 0 || slub_debug_string)
                static_branch_enable(&slub_debug_enabled);
        else
@@ -1582,20 +1591,8 @@ static __always_inline bool slab_free_hook(struct kmem_cache *s,
 {
        kmemleak_free_recursive(x, s->flags);
 
-       /*
-        * Trouble is that we may no longer disable interrupts in the fast path
-        * So in order to make the debug calls that expect irqs to be
-        * disabled we need to disable interrupts temporarily.
-        */
-#ifdef CONFIG_LOCKDEP
-       {
-               unsigned long flags;
+       debug_check_no_locks_freed(x, s->object_size);
 
-               local_irq_save(flags);
-               debug_check_no_locks_freed(x, s->object_size);
-               local_irq_restore(flags);
-       }
-#endif
        if (!(s->flags & SLAB_DEBUG_OBJECTS))
                debug_check_no_obj_freed(x, s->object_size);
 
@@ -2469,13 +2466,6 @@ static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
 
        } while (this_cpu_cmpxchg(s->cpu_slab->partial, oldpage, page)
                                                                != oldpage);
-       if (unlikely(!slub_cpu_partial(s))) {
-               unsigned long flags;
-
-               local_irq_save(flags);
-               unfreeze_partials(s, this_cpu_ptr(s->cpu_slab));
-               local_irq_restore(flags);
-       }
        preempt_enable();
 #endif /* CONFIG_SLUB_CPU_PARTIAL */
 }
@@ -2623,17 +2613,12 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid)
 static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
                        int node, struct kmem_cache_cpu **pc)
 {
-       void *freelist;
+       void *freelist = NULL;
        struct kmem_cache_cpu *c = *pc;
        struct page *page;
 
        WARN_ON_ONCE(s->ctor && (flags & __GFP_ZERO));
 
-       freelist = get_partial(s, flags, node, c);
-
-       if (freelist)
-               return freelist;
-
        page = new_slab(s, flags, node);
        if (page) {
                c = raw_cpu_ptr(s->cpu_slab);
@@ -2797,6 +2782,10 @@ new_slab:
                goto redo;
        }
 
+       freelist = get_partial(s, gfpflags, node, c);
+       if (freelist)
+               goto check_new_page;
+
        freelist = new_slab_objects(s, gfpflags, node, &c);
 
        if (unlikely(!freelist)) {
@@ -2804,6 +2793,7 @@ new_slab:
                return NULL;
        }
 
+check_new_page:
        page = c->page;
        if (likely(!kmem_cache_debug(s) && pfmemalloc_match(page, gfpflags)))
                goto load_freelist;
@@ -3236,12 +3226,12 @@ struct detached_freelist {
        struct kmem_cache *s;
 };
 
-static inline void free_nonslab_page(struct page *page)
+static inline void free_nonslab_page(struct page *page, void *object)
 {
        unsigned int order = compound_order(page);
 
        VM_BUG_ON_PAGE(!PageCompound(page), page);
-       kfree_hook(page_address(page));
+       kfree_hook(object);
        mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B, -(PAGE_SIZE << order));
        __free_pages(page, order);
 }
@@ -3282,7 +3272,7 @@ int build_detached_freelist(struct kmem_cache *s, size_t size,
        if (!s) {
                /* Handle kalloc'ed objects */
                if (unlikely(!PageSlab(page))) {
-                       free_nonslab_page(page);
+                       free_nonslab_page(page, object);
                        p[size] = NULL; /* mark object processed */
                        return size;
                }
@@ -4258,7 +4248,7 @@ void kfree(const void *x)
 
        page = virt_to_head_page(x);
        if (unlikely(!PageSlab(page))) {
-               free_nonslab_page(page);
+               free_nonslab_page(page, object);
                return;
        }
        slab_free(page->slab_cache, page, object, NULL, 1, _RET_IP_);
@@ -4670,11 +4660,11 @@ static int count_total(struct page *page)
 #endif
 
 #ifdef CONFIG_SLUB_DEBUG
-static void validate_slab(struct kmem_cache *s, struct page *page)
+static void validate_slab(struct kmem_cache *s, struct page *page,
+                         unsigned long *obj_map)
 {
        void *p;
        void *addr = page_address(page);
-       unsigned long *map;
 
        slab_lock(page);
 
@@ -4682,21 +4672,20 @@ static void validate_slab(struct kmem_cache *s, struct page *page)
                goto unlock;
 
        /* Now we know that a valid freelist exists */
-       map = get_map(s, page);
+       __fill_map(obj_map, s, page);
        for_each_object(p, s, addr, page->objects) {
-               u8 val = test_bit(__obj_to_index(s, addr, p), map) ?
+               u8 val = test_bit(__obj_to_index(s, addr, p), obj_map) ?
                         SLUB_RED_INACTIVE : SLUB_RED_ACTIVE;
 
                if (!check_object(s, page, p, val))
                        break;
        }
-       put_map(map);
 unlock:
        slab_unlock(page);
 }
 
 static int validate_slab_node(struct kmem_cache *s,
-               struct kmem_cache_node *n)
+               struct kmem_cache_node *n, unsigned long *obj_map)
 {
        unsigned long count = 0;
        struct page *page;
@@ -4705,7 +4694,7 @@ static int validate_slab_node(struct kmem_cache *s,
        spin_lock_irqsave(&n->list_lock, flags);
 
        list_for_each_entry(page, &n->partial, slab_list) {
-               validate_slab(s, page);
+               validate_slab(s, page, obj_map);
                count++;
        }
        if (count != n->nr_partial) {
@@ -4718,7 +4707,7 @@ static int validate_slab_node(struct kmem_cache *s,
                goto out;
 
        list_for_each_entry(page, &n->full, slab_list) {
-               validate_slab(s, page);
+               validate_slab(s, page, obj_map);
                count++;
        }
        if (count != atomic_long_read(&n->nr_slabs)) {
@@ -4737,10 +4726,17 @@ long validate_slab_cache(struct kmem_cache *s)
        int node;
        unsigned long count = 0;
        struct kmem_cache_node *n;
+       unsigned long *obj_map;
+
+       obj_map = bitmap_alloc(oo_objects(s->oo), GFP_KERNEL);
+       if (!obj_map)
+               return -ENOMEM;
 
        flush_all(s);
        for_each_kmem_cache_node(s, node, n)
-               count += validate_slab_node(s, n);
+               count += validate_slab_node(s, n, obj_map);
+
+       bitmap_free(obj_map);
 
        return count;
 }
@@ -4876,17 +4872,17 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
 }
 
 static void process_slab(struct loc_track *t, struct kmem_cache *s,
-               struct page *page, enum track_item alloc)
+               struct page *page, enum track_item alloc,
+               unsigned long *obj_map)
 {
        void *addr = page_address(page);
        void *p;
-       unsigned long *map;
 
-       map = get_map(s, page);
+       __fill_map(obj_map, s, page);
+
        for_each_object(p, s, addr, page->objects)
-               if (!test_bit(__obj_to_index(s, addr, p), map))
+               if (!test_bit(__obj_to_index(s, addr, p), obj_map))
                        add_location(t, s, get_track(s, p, alloc));
-       put_map(map);
 }
 #endif  /* CONFIG_DEBUG_FS   */
 #endif /* CONFIG_SLUB_DEBUG */
@@ -5813,17 +5809,21 @@ static int slab_debug_trace_open(struct inode *inode, struct file *filep)
        struct loc_track *t = __seq_open_private(filep, &slab_debugfs_sops,
                                                sizeof(struct loc_track));
        struct kmem_cache *s = file_inode(filep)->i_private;
+       unsigned long *obj_map;
+
+       obj_map = bitmap_alloc(oo_objects(s->oo), GFP_KERNEL);
+       if (!obj_map)
+               return -ENOMEM;
 
        if (strcmp(filep->f_path.dentry->d_name.name, "alloc_traces") == 0)
                alloc = TRACK_ALLOC;
        else
                alloc = TRACK_FREE;
 
-       if (!alloc_loc_track(t, PAGE_SIZE / sizeof(struct location), GFP_KERNEL))
+       if (!alloc_loc_track(t, PAGE_SIZE / sizeof(struct location), GFP_KERNEL)) {
+               bitmap_free(obj_map);
                return -ENOMEM;
-
-       /* Push back cpu slabs */
-       flush_all(s);
+       }
 
        for_each_kmem_cache_node(s, node, n) {
                unsigned long flags;
@@ -5834,12 +5834,13 @@ static int slab_debug_trace_open(struct inode *inode, struct file *filep)
 
                spin_lock_irqsave(&n->list_lock, flags);
                list_for_each_entry(page, &n->partial, slab_list)
-                       process_slab(t, s, page, alloc);
+                       process_slab(t, s, page, alloc, obj_map);
                list_for_each_entry(page, &n->full, slab_list)
-                       process_slab(t, s, page, alloc);
+                       process_slab(t, s, page, alloc, obj_map);
                spin_unlock_irqrestore(&n->list_lock, flags);
        }
 
+       bitmap_free(obj_map);
        return 0;
 }