Skip to content

Commit

Permalink
v2 mm/slub: restore/expand unfreeze_partials() local exclusion scope
Browse files Browse the repository at this point in the history
On Thu, 2021-07-15 at 18:34 +0200, Mike Galbraith wrote:
> Greetings crickets,
>
> Methinks he problem is the hole these patches opened only for RT.
>
> static void put_cpu_partial(struct kmem_cache *s, struct page *page,
> int drain)
> {
> #ifdef CONFIG_SLUB_CPU_PARTIAL
> 	struct page *oldpage;
> 	int pages;
> 	int pobjects;
>
> 	slub_get_cpu_ptr(s->cpu_slab);
>         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Bah, I'm tired of waiting to see what if anything mm folks do about
this little bugger, so I'm gonna step on it my damn self and be done
with it.  Fly or die little patchlet.

mm/slub: restore/expand unfreeze_partials() local exclusion scope

2180da7 ("mm, slub: use migrate_disable() on PREEMPT_RT") replaced
preempt_disable() in put_cpu_partial() with migrate_disable(), which when
combined with ___slab_alloc() having become preemptibile, leads to
kmem_cache_free()/kfree() blowing through ___slab_alloc() unimpeded,
and vice versa, resulting in PREMPT_RT exclusive explosions in both
paths while stress testing with both SLUB_CPU_PARTIAL/MEMCG enabled,
___slab_alloc() during allocation (duh), and __unfreeze_partials()
during free, both while accessing an unmapped page->freelist.

Serialize put_cpu_partial()/unfreeze_partials() on cpu_slab->lock to
ensure that alloc/free paths cannot pluck cpu_slab->partial out from
underneath each other unconstrained.

Signed-off-by: Mike Galbraith <efault@gmx.de>
Fixes: 2180da7 ("mm, slub: use migrate_disable() on PREEMPT_RT")
  • Loading branch information
Mike Galbraith authored and intel-lab-lkp committed Jul 18, 2021
1 parent 87ca007 commit 9398c1a
Showing 1 changed file with 22 additions and 1 deletion.
23 changes: 22 additions & 1 deletion mm/slub.c
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,17 @@ static void __unfreeze_partials(struct kmem_cache *s, struct page *partial_page)
if (n)
spin_unlock_irqrestore(&n->list_lock, flags);

/*
* If we got here via __slab_free() -> put_cpu_partial(),
* memcg_free_page_obj_cgroups() ->kfree() may send us
* back to __slab_free() -> put_cpu_partial() for an
* unfortunate second encounter with cpu_slab->lock.
*/
if (IS_ENABLED(CONFIG_PREEMPT_RT) && memcg_kmem_enabled()) {
lockdep_assert_held(this_cpu_ptr(&s->cpu_slab->lock.lock));
local_unlock(&s->cpu_slab->lock);
}

while (discard_page) {
page = discard_page;
discard_page = discard_page->next;
Expand All @@ -2426,6 +2437,9 @@ static void __unfreeze_partials(struct kmem_cache *s, struct page *partial_page)
discard_slab(s, page);
stat(s, FREE_SLAB);
}

if (IS_ENABLED(CONFIG_PREEMPT_RT) && memcg_kmem_enabled())
local_lock(&s->cpu_slab->lock);
}

/*
Expand Down Expand Up @@ -2497,7 +2511,9 @@ static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
* partial array is full. Move the existing
* set to the per node partial list.
*/
local_lock(&s->cpu_slab->lock);
unfreeze_partials(s);
local_unlock(&s->cpu_slab->lock);
oldpage = NULL;
pobjects = 0;
pages = 0;
Expand Down Expand Up @@ -2579,7 +2595,9 @@ static void flush_cpu_slab(struct work_struct *w)
if (c->page)
flush_slab(s, c, true);

local_lock(&s->cpu_slab->lock);
unfreeze_partials(s);
local_unlock(&s->cpu_slab->lock);
}

static bool has_cpu_slab(int cpu, struct kmem_cache *s)
Expand Down Expand Up @@ -2632,8 +2650,11 @@ static int slub_cpu_dead(unsigned int cpu)
struct kmem_cache *s;

mutex_lock(&slab_mutex);
list_for_each_entry(s, &slab_caches, list)
list_for_each_entry(s, &slab_caches, list) {
local_lock(&s->cpu_slab->lock);
__flush_cpu_slab(s, cpu);
local_unlock(&s->cpu_slab->lock);
}
mutex_unlock(&slab_mutex);
return 0;
}
Expand Down

0 comments on commit 9398c1a

Please sign in to comment.