Skip to content

Commit e500059

Browse files
committed
mm, slub: move disabling/enabling irqs to ___slab_alloc()
Currently __slab_alloc() disables irqs around the whole ___slab_alloc(). This includes cases where this is not needed, such as when the allocation ends up in the page allocator and has to awkwardly enable irqs back based on gfp flags. Also the whole kmem_cache_alloc_bulk() is executed with irqs disabled even when it hits the __slab_alloc() slow path, and long periods with disabled interrupts are undesirable. As a first step towards reducing irq disabled periods, move irq handling into ___slab_alloc(). Callers will instead prevent the s->cpu_slab percpu pointer from becoming invalid via get_cpu_ptr(), thus preempt_disable(). This does not protect against modification by an irq handler, which is still done by disabled irq for most of ___slab_alloc(). As a small immediate benefit, slab_out_of_memory() from ___slab_alloc() is now called with irqs enabled. kmem_cache_alloc_bulk() disables irqs for its fastpath and then re-enables them before calling ___slab_alloc(), which then disables them at its discretion. The whole kmem_cache_alloc_bulk() operation also disables preemption. When ___slab_alloc() calls new_slab() to allocate a new page, re-enable preemption, because new_slab() will re-enable interrupts in contexts that allow blocking (this will be improved by later patches). The patch itself will thus increase overhead a bit due to disabled preemption (on configs where it matters) and increased disabling/enabling irqs in kmem_cache_alloc_bulk(), but that will be gradually improved in the following patches. Note in __slab_alloc() we need to change the #ifdef CONFIG_PREEMPT guard to CONFIG_PREEMPT_COUNT to make sure preempt disable/enable is properly paired in all configurations. On configs without involuntary preemption and debugging the re-read of kmem_cache_cpu pointer is still compiled out as it was before. [ Mike Galbraith <[email protected]>: Fix kmem_cache_alloc_bulk() error path ] Signed-off-by: Vlastimil Babka <[email protected]>
1 parent 9b4bc85 commit e500059

File tree

1 file changed

+24
-12
lines changed

1 file changed

+24
-12
lines changed

mm/slub.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,17 +2670,19 @@ static inline void *get_freelist(struct kmem_cache *s, struct page *page)
26702670
* we need to allocate a new slab. This is the slowest path since it involves
26712671
* a call to the page allocator and the setup of a new slab.
26722672
*
2673-
* Version of __slab_alloc to use when we know that interrupts are
2673+
* Version of __slab_alloc to use when we know that preemption is
26742674
* already disabled (which is the case for bulk allocation).
26752675
*/
26762676
static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
26772677
unsigned long addr, struct kmem_cache_cpu *c)
26782678
{
26792679
void *freelist;
26802680
struct page *page;
2681+
unsigned long flags;
26812682

26822683
stat(s, ALLOC_SLOWPATH);
26832684

2685+
local_irq_save(flags);
26842686
page = c->page;
26852687
if (!page) {
26862688
/*
@@ -2743,6 +2745,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27432745
VM_BUG_ON(!c->page->frozen);
27442746
c->freelist = get_freepointer(s, freelist);
27452747
c->tid = next_tid(c->tid);
2748+
local_irq_restore(flags);
27462749
return freelist;
27472750

27482751
new_slab:
@@ -2760,14 +2763,16 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27602763
goto check_new_page;
27612764
}
27622765

2766+
put_cpu_ptr(s->cpu_slab);
27632767
page = new_slab(s, gfpflags, node);
2768+
c = get_cpu_ptr(s->cpu_slab);
27642769

27652770
if (unlikely(!page)) {
2771+
local_irq_restore(flags);
27662772
slab_out_of_memory(s, gfpflags, node);
27672773
return NULL;
27682774
}
27692775

2770-
c = raw_cpu_ptr(s->cpu_slab);
27712776
if (c->page)
27722777
flush_slab(s, c);
27732778

@@ -2807,31 +2812,33 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
28072812
return_single:
28082813

28092814
deactivate_slab(s, page, get_freepointer(s, freelist), c);
2815+
local_irq_restore(flags);
28102816
return freelist;
28112817
}
28122818

28132819
/*
2814-
* Another one that disabled interrupt and compensates for possible
2815-
* cpu changes by refetching the per cpu area pointer.
2820+
* A wrapper for ___slab_alloc() for contexts where preemption is not yet
2821+
* disabled. Compensates for possible cpu changes by refetching the per cpu area
2822+
* pointer.
28162823
*/
28172824
static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
28182825
unsigned long addr, struct kmem_cache_cpu *c)
28192826
{
28202827
void *p;
2821-
unsigned long flags;
28222828

2823-
local_irq_save(flags);
2824-
#ifdef CONFIG_PREEMPTION
2829+
#ifdef CONFIG_PREEMPT_COUNT
28252830
/*
28262831
* We may have been preempted and rescheduled on a different
2827-
* cpu before disabling interrupts. Need to reload cpu area
2832+
* cpu before disabling preemption. Need to reload cpu area
28282833
* pointer.
28292834
*/
2830-
c = this_cpu_ptr(s->cpu_slab);
2835+
c = get_cpu_ptr(s->cpu_slab);
28312836
#endif
28322837

28332838
p = ___slab_alloc(s, gfpflags, node, addr, c);
2834-
local_irq_restore(flags);
2839+
#ifdef CONFIG_PREEMPT_COUNT
2840+
put_cpu_ptr(s->cpu_slab);
2841+
#endif
28352842
return p;
28362843
}
28372844

@@ -3359,8 +3366,8 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
33593366
* IRQs, which protects against PREEMPT and interrupts
33603367
* handlers invoking normal fastpath.
33613368
*/
3369+
c = get_cpu_ptr(s->cpu_slab);
33623370
local_irq_disable();
3363-
c = this_cpu_ptr(s->cpu_slab);
33643371

33653372
for (i = 0; i < size; i++) {
33663373
void *object = kfence_alloc(s, s->object_size, flags);
@@ -3381,6 +3388,8 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
33813388
*/
33823389
c->tid = next_tid(c->tid);
33833390

3391+
local_irq_enable();
3392+
33843393
/*
33853394
* Invoking slow path likely have side-effect
33863395
* of re-populating per CPU c->freelist
@@ -3393,6 +3402,8 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
33933402
c = this_cpu_ptr(s->cpu_slab);
33943403
maybe_wipe_obj_freeptr(s, p[i]);
33953404

3405+
local_irq_disable();
3406+
33963407
continue; /* goto for-loop */
33973408
}
33983409
c->freelist = get_freepointer(s, object);
@@ -3401,6 +3412,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
34013412
}
34023413
c->tid = next_tid(c->tid);
34033414
local_irq_enable();
3415+
put_cpu_ptr(s->cpu_slab);
34043416

34053417
/*
34063418
* memcg and kmem_cache debug support and memory initialization.
@@ -3410,7 +3422,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
34103422
slab_want_init_on_alloc(flags, s));
34113423
return i;
34123424
error:
3413-
local_irq_enable();
3425+
put_cpu_ptr(s->cpu_slab);
34143426
slab_post_alloc_hook(s, objcg, flags, i, p, false);
34153427
__kmem_cache_free_bulk(s, i, p);
34163428
return 0;

0 commit comments

Comments
 (0)