Skip to content

Commit 479f854

Browse files
gormanmtorvalds
authored andcommitted
mm, page_alloc: defer debugging checks of pages allocated from the PCP
Every page allocated checks a number of page fields for validity. This catches corruption bugs of pages that are already freed but it is expensive. This patch weakens the debugging check by checking PCP pages only when the PCP lists are being refilled. All compound pages are checked. This potentially avoids debugging checks entirely if the PCP lists are never emptied and refilled so some corruption issues may be missed. Full checking requires DEBUG_VM. With the two deferred debugging patches applied, the impact to a page allocator microbenchmark is 4.6.0-rc3 4.6.0-rc3 inline-v3r6 deferalloc-v3r7 Min alloc-odr0-1 344.00 ( 0.00%) 317.00 ( 7.85%) Min alloc-odr0-2 248.00 ( 0.00%) 231.00 ( 6.85%) Min alloc-odr0-4 209.00 ( 0.00%) 192.00 ( 8.13%) Min alloc-odr0-8 181.00 ( 0.00%) 166.00 ( 8.29%) Min alloc-odr0-16 168.00 ( 0.00%) 154.00 ( 8.33%) Min alloc-odr0-32 161.00 ( 0.00%) 148.00 ( 8.07%) Min alloc-odr0-64 158.00 ( 0.00%) 145.00 ( 8.23%) Min alloc-odr0-128 156.00 ( 0.00%) 143.00 ( 8.33%) Min alloc-odr0-256 168.00 ( 0.00%) 154.00 ( 8.33%) Min alloc-odr0-512 178.00 ( 0.00%) 167.00 ( 6.18%) Min alloc-odr0-1024 186.00 ( 0.00%) 174.00 ( 6.45%) Min alloc-odr0-2048 192.00 ( 0.00%) 180.00 ( 6.25%) Min alloc-odr0-4096 198.00 ( 0.00%) 184.00 ( 7.07%) Min alloc-odr0-8192 200.00 ( 0.00%) 188.00 ( 6.00%) Min alloc-odr0-16384 201.00 ( 0.00%) 188.00 ( 6.47%) Min free-odr0-1 189.00 ( 0.00%) 180.00 ( 4.76%) Min free-odr0-2 132.00 ( 0.00%) 126.00 ( 4.55%) Min free-odr0-4 104.00 ( 0.00%) 99.00 ( 4.81%) Min free-odr0-8 90.00 ( 0.00%) 85.00 ( 5.56%) Min free-odr0-16 84.00 ( 0.00%) 80.00 ( 4.76%) Min free-odr0-32 80.00 ( 0.00%) 76.00 ( 5.00%) Min free-odr0-64 78.00 ( 0.00%) 74.00 ( 5.13%) Min free-odr0-128 77.00 ( 0.00%) 73.00 ( 5.19%) Min free-odr0-256 94.00 ( 0.00%) 91.00 ( 3.19%) Min free-odr0-512 108.00 ( 0.00%) 112.00 ( -3.70%) Min free-odr0-1024 115.00 ( 0.00%) 118.00 ( -2.61%) Min free-odr0-2048 120.00 ( 0.00%) 125.00 ( -4.17%) Min free-odr0-4096 123.00 ( 0.00%) 129.00 ( -4.88%) Min free-odr0-8192 126.00 ( 0.00%) 130.00 ( -3.17%) Min free-odr0-16384 126.00 ( 0.00%) 131.00 ( -3.97%) Note that the free paths for large numbers of pages is impacted as the debugging cost gets shifted into that path when the page data is no longer necessarily cache-hot. Signed-off-by: Mel Gorman <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Cc: Jesper Dangaard Brouer <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 4db7548 commit 479f854

File tree

1 file changed

+64
-28
lines changed

1 file changed

+64
-28
lines changed

mm/page_alloc.c

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,16 +1714,48 @@ static inline bool free_pages_prezeroed(bool poisoned)
17141714
page_poisoning_enabled() && poisoned;
17151715
}
17161716

1717-
static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
1717+
#ifdef CONFIG_DEBUG_VM
1718+
static bool check_pcp_refill(struct page *page)
1719+
{
1720+
return false;
1721+
}
1722+
1723+
static bool check_new_pcp(struct page *page)
1724+
{
1725+
return check_new_page(page);
1726+
}
1727+
#else
1728+
static bool check_pcp_refill(struct page *page)
1729+
{
1730+
return check_new_page(page);
1731+
}
1732+
static bool check_new_pcp(struct page *page)
1733+
{
1734+
return false;
1735+
}
1736+
#endif /* CONFIG_DEBUG_VM */
1737+
1738+
static bool check_new_pages(struct page *page, unsigned int order)
1739+
{
1740+
int i;
1741+
for (i = 0; i < (1 << order); i++) {
1742+
struct page *p = page + i;
1743+
1744+
if (unlikely(check_new_page(p)))
1745+
return true;
1746+
}
1747+
1748+
return false;
1749+
}
1750+
1751+
static void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
17181752
unsigned int alloc_flags)
17191753
{
17201754
int i;
17211755
bool poisoned = true;
17221756

17231757
for (i = 0; i < (1 << order); i++) {
17241758
struct page *p = page + i;
1725-
if (unlikely(check_new_page(p)))
1726-
return 1;
17271759
if (poisoned)
17281760
poisoned &= page_is_poisoned(p);
17291761
}
@@ -1755,8 +1787,6 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
17551787
set_page_pfmemalloc(page);
17561788
else
17571789
clear_page_pfmemalloc(page);
1758-
1759-
return 0;
17601790
}
17611791

17621792
/*
@@ -2178,6 +2208,9 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
21782208
if (unlikely(page == NULL))
21792209
break;
21802210

2211+
if (unlikely(check_pcp_refill(page)))
2212+
continue;
2213+
21812214
/*
21822215
* Split buddy pages returned by expand() are received here
21832216
* in physical page order. The page is added to the callers and
@@ -2593,20 +2626,22 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
25932626
struct list_head *list;
25942627

25952628
local_irq_save(flags);
2596-
pcp = &this_cpu_ptr(zone->pageset)->pcp;
2597-
list = &pcp->lists[migratetype];
2598-
if (list_empty(list)) {
2599-
pcp->count += rmqueue_bulk(zone, 0,
2600-
pcp->batch, list,
2601-
migratetype, cold);
2602-
if (unlikely(list_empty(list)))
2603-
goto failed;
2604-
}
2629+
do {
2630+
pcp = &this_cpu_ptr(zone->pageset)->pcp;
2631+
list = &pcp->lists[migratetype];
2632+
if (list_empty(list)) {
2633+
pcp->count += rmqueue_bulk(zone, 0,
2634+
pcp->batch, list,
2635+
migratetype, cold);
2636+
if (unlikely(list_empty(list)))
2637+
goto failed;
2638+
}
26052639

2606-
if (cold)
2607-
page = list_last_entry(list, struct page, lru);
2608-
else
2609-
page = list_first_entry(list, struct page, lru);
2640+
if (cold)
2641+
page = list_last_entry(list, struct page, lru);
2642+
else
2643+
page = list_first_entry(list, struct page, lru);
2644+
} while (page && check_new_pcp(page));
26102645

26112646
__dec_zone_state(zone, NR_ALLOC_BATCH);
26122647
list_del(&page->lru);
@@ -2619,14 +2654,16 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
26192654
WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
26202655
spin_lock_irqsave(&zone->lock, flags);
26212656

2622-
page = NULL;
2623-
if (alloc_flags & ALLOC_HARDER) {
2624-
page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
2625-
if (page)
2626-
trace_mm_page_alloc_zone_locked(page, order, migratetype);
2627-
}
2628-
if (!page)
2629-
page = __rmqueue(zone, order, migratetype);
2657+
do {
2658+
page = NULL;
2659+
if (alloc_flags & ALLOC_HARDER) {
2660+
page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
2661+
if (page)
2662+
trace_mm_page_alloc_zone_locked(page, order, migratetype);
2663+
}
2664+
if (!page)
2665+
page = __rmqueue(zone, order, migratetype);
2666+
} while (page && check_new_pages(page, order));
26302667
spin_unlock(&zone->lock);
26312668
if (!page)
26322669
goto failed;
@@ -2993,8 +3030,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
29933030
page = buffered_rmqueue(ac->preferred_zoneref->zone, zone, order,
29943031
gfp_mask, alloc_flags, ac->migratetype);
29953032
if (page) {
2996-
if (prep_new_page(page, order, gfp_mask, alloc_flags))
2997-
goto try_this_zone;
3033+
prep_new_page(page, order, gfp_mask, alloc_flags);
29983034

29993035
/*
30003036
* If this is a high-order atomic allocation then check

0 commit comments

Comments
 (0)