Skip to content

Commit 0b303fb

Browse files
committed
mm, slub: do initial checks in ___slab_alloc() with irqs enabled
As another step of shortening irq disabled sections in ___slab_alloc(), delay disabling irqs until we pass the initial checks if there is a cached percpu slab and it's suitable for our allocation. Now we have to recheck c->page after actually disabling irqs as an allocation in irq handler might have replaced it. Because we call pfmemalloc_match() as one of the checks, we might hit VM_BUG_ON_PAGE(!PageSlab(page)) in PageSlabPfmemalloc in case we get interrupted and the page is freed. Thus introduce a pfmemalloc_match_unsafe() variant that lacks the PageSlab check. Signed-off-by: Vlastimil Babka <[email protected]> Acked-by: Mel Gorman <[email protected]>
1 parent e500059 commit 0b303fb

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

include/linux/page-flags.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,15 @@ static inline int PageSlabPfmemalloc(struct page *page)
815815
return PageActive(page);
816816
}
817817

818+
/*
819+
* A version of PageSlabPfmemalloc() for opportunistic checks where the page
820+
* might have been freed under us and not be a PageSlab anymore.
821+
*/
822+
static inline int __PageSlabPfmemalloc(struct page *page)
823+
{
824+
return PageActive(page);
825+
}
826+
818827
static inline void SetPageSlabPfmemalloc(struct page *page)
819828
{
820829
VM_BUG_ON_PAGE(!PageSlab(page), page);

mm/slub.c

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2620,6 +2620,19 @@ static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags)
26202620
return true;
26212621
}
26222622

2623+
/*
2624+
* A variant of pfmemalloc_match() that tests page flags without asserting
2625+
* PageSlab. Intended for opportunistic checks before taking a lock and
2626+
* rechecking that nobody else freed the page under us.
2627+
*/
2628+
static inline bool pfmemalloc_match_unsafe(struct page *page, gfp_t gfpflags)
2629+
{
2630+
if (unlikely(__PageSlabPfmemalloc(page)))
2631+
return gfp_pfmemalloc_allowed(gfpflags);
2632+
2633+
return true;
2634+
}
2635+
26232636
/*
26242637
* Check the page->freelist of a page and either transfer the freelist to the
26252638
* per cpu freelist or deactivate the page.
@@ -2682,8 +2695,9 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
26822695

26832696
stat(s, ALLOC_SLOWPATH);
26842697

2685-
local_irq_save(flags);
2686-
page = c->page;
2698+
reread_page:
2699+
2700+
page = READ_ONCE(c->page);
26872701
if (!page) {
26882702
/*
26892703
* if the node is not online or has no normal memory, just
@@ -2692,6 +2706,11 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
26922706
if (unlikely(node != NUMA_NO_NODE &&
26932707
!node_isset(node, slab_nodes)))
26942708
node = NUMA_NO_NODE;
2709+
local_irq_save(flags);
2710+
if (unlikely(c->page)) {
2711+
local_irq_restore(flags);
2712+
goto reread_page;
2713+
}
26952714
goto new_slab;
26962715
}
26972716
redo:
@@ -2706,8 +2725,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27062725
goto redo;
27072726
} else {
27082727
stat(s, ALLOC_NODE_MISMATCH);
2709-
deactivate_slab(s, page, c->freelist, c);
2710-
goto new_slab;
2728+
goto deactivate_slab;
27112729
}
27122730
}
27132731

@@ -2716,12 +2734,15 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27162734
* PFMEMALLOC but right now, we are losing the pfmemalloc
27172735
* information when the page leaves the per-cpu allocator
27182736
*/
2719-
if (unlikely(!pfmemalloc_match(page, gfpflags))) {
2720-
deactivate_slab(s, page, c->freelist, c);
2721-
goto new_slab;
2722-
}
2737+
if (unlikely(!pfmemalloc_match_unsafe(page, gfpflags)))
2738+
goto deactivate_slab;
27232739

2724-
/* must check again c->freelist in case of cpu migration or IRQ */
2740+
/* must check again c->page in case IRQ handler changed it */
2741+
local_irq_save(flags);
2742+
if (unlikely(page != c->page)) {
2743+
local_irq_restore(flags);
2744+
goto reread_page;
2745+
}
27252746
freelist = c->freelist;
27262747
if (freelist)
27272748
goto load_freelist;
@@ -2737,6 +2758,9 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27372758
stat(s, ALLOC_REFILL);
27382759

27392760
load_freelist:
2761+
2762+
lockdep_assert_irqs_disabled();
2763+
27402764
/*
27412765
* freelist is pointing to the list of objects to be used.
27422766
* page is pointing to the page from which the objects are obtained.
@@ -2748,11 +2772,23 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
27482772
local_irq_restore(flags);
27492773
return freelist;
27502774

2775+
deactivate_slab:
2776+
2777+
local_irq_save(flags);
2778+
if (page != c->page) {
2779+
local_irq_restore(flags);
2780+
goto reread_page;
2781+
}
2782+
deactivate_slab(s, page, c->freelist, c);
2783+
27512784
new_slab:
27522785

2786+
lockdep_assert_irqs_disabled();
2787+
27532788
if (slub_percpu_partial(c)) {
27542789
page = c->page = slub_percpu_partial(c);
27552790
slub_set_percpu_partial(c, page);
2791+
local_irq_restore(flags);
27562792
stat(s, CPU_PARTIAL_ALLOC);
27572793
goto redo;
27582794
}

0 commit comments

Comments
 (0)