Skip to content

Commit

Permalink
Split IRQ-off and zone->lock while freeing pages from PCP list #2
Browse files Browse the repository at this point in the history
Split the IRQ-off section while accessing the PCP list from zone->lock
while freeing pages.
Introcude  isolate_pcp_pages() which separates the pages from the PCP
list onto a temporary list and then free the temporary list via
free_pcppages_bulk().

Signed-off-by: Peter Zijlstra <[email protected]>
Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
  • Loading branch information
Peter Zijlstra authored and Sebastian Andrzej Siewior committed Oct 28, 2020
1 parent c2ced72 commit 2f47b7b
Showing 1 changed file with 50 additions and 10 deletions.
60 changes: 50 additions & 10 deletions mm/page_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1339,8 +1339,8 @@ static inline void prefetch_buddy(struct page *page)
* And clear the zone's pages_scanned counter, to hold off the "all pages are
* pinned" detection logic.
*/
static void free_pcppages_bulk(struct zone *zone, int count,
struct list_head *head)
static void free_pcppages_bulk(struct zone *zone, struct list_head *head,
bool zone_retry)
{
bool isolated_pageblocks;
struct page *page, *tmp;
Expand All @@ -1355,12 +1355,27 @@ static void free_pcppages_bulk(struct zone *zone, int count,
*/
list_for_each_entry_safe(page, tmp, head, lru) {
int mt = get_pcppage_migratetype(page);

if (page_zone(page) != zone) {
/*
* free_unref_page_list() sorts pages by zone. If we end
* up with pages from a different NUMA nodes belonging
* to the same ZONE index then we need to redo with the
* correct ZONE pointer. Skip the page for now, redo it
* on the next iteration.
*/
WARN_ON_ONCE(zone_retry == false);
if (zone_retry)
continue;
}

/* MIGRATE_ISOLATE page should not go to pcplists */
VM_BUG_ON_PAGE(is_migrate_isolate(mt), page);
/* Pageblock could have been isolated meanwhile */
if (unlikely(isolated_pageblocks))
mt = get_pageblock_migratetype(page);

list_del(&page->lru);
__free_one_page(page, page_to_pfn(page), zone, 0, mt, FPI_NONE);
trace_mm_page_pcpu_drain(page, 0, mt);
}
Expand Down Expand Up @@ -2951,7 +2966,7 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
local_irq_restore(flags);

if (to_drain > 0)
free_pcppages_bulk(zone, to_drain, &dst);
free_pcppages_bulk(zone, &dst, false);
}
#endif

Expand Down Expand Up @@ -2981,7 +2996,7 @@ static void drain_pages_zone(unsigned int cpu, struct zone *zone)
local_irq_restore(flags);

if (count)
free_pcppages_bulk(zone, count, &dst);
free_pcppages_bulk(zone, &dst, false);
}

/*
Expand Down Expand Up @@ -3180,7 +3195,8 @@ static bool free_unref_page_prepare(struct page *page, unsigned long pfn)
return true;
}

static void free_unref_page_commit(struct page *page, unsigned long pfn)
static void free_unref_page_commit(struct page *page, unsigned long pfn,
struct list_head *dst)
{
struct zone *zone = page_zone(page);
struct per_cpu_pages *pcp;
Expand Down Expand Up @@ -3210,10 +3226,8 @@ static void free_unref_page_commit(struct page *page, unsigned long pfn)
pcp->count++;
if (pcp->count >= pcp->high) {
unsigned long batch = READ_ONCE(pcp->batch);
LIST_HEAD(dst);

isolate_pcp_pages(batch, pcp, &dst);
free_pcppages_bulk(zone, batch, &dst);
isolate_pcp_pages(batch, pcp, dst);
}
}

Expand All @@ -3224,13 +3238,17 @@ void free_unref_page(struct page *page)
{
unsigned long flags;
unsigned long pfn = page_to_pfn(page);
struct zone *zone = page_zone(page);
LIST_HEAD(dst);

if (!free_unref_page_prepare(page, pfn))
return;

local_irq_save(flags);
free_unref_page_commit(page, pfn);
free_unref_page_commit(page, pfn, &dst);
local_irq_restore(flags);
if (!list_empty(&dst))
free_pcppages_bulk(zone, &dst, false);
}

/*
Expand All @@ -3241,6 +3259,11 @@ void free_unref_page_list(struct list_head *list)
struct page *page, *next;
unsigned long flags, pfn;
int batch_count = 0;
struct list_head dsts[__MAX_NR_ZONES];
int i;

for (i = 0; i < __MAX_NR_ZONES; i++)
INIT_LIST_HEAD(&dsts[i]);

/* Prepare pages for freeing */
list_for_each_entry_safe(page, next, list, lru) {
Expand All @@ -3253,10 +3276,12 @@ void free_unref_page_list(struct list_head *list)
local_irq_save(flags);
list_for_each_entry_safe(page, next, list, lru) {
unsigned long pfn = page_private(page);
enum zone_type type;

set_page_private(page, 0);
trace_mm_page_free_batched(page);
free_unref_page_commit(page, pfn);
type = page_zonenum(page);
free_unref_page_commit(page, pfn, &dsts[type]);

/*
* Guard against excessive IRQ disabled times when we get
Expand All @@ -3269,6 +3294,21 @@ void free_unref_page_list(struct list_head *list)
}
}
local_irq_restore(flags);

for (i = 0; i < __MAX_NR_ZONES; ) {
struct page *page;
struct zone *zone;

if (list_empty(&dsts[i])) {
i++;
continue;
}

page = list_first_entry(&dsts[i], struct page, lru);
zone = page_zone(page);

free_pcppages_bulk(zone, &dsts[i], true);
}
}

/*
Expand Down

0 comments on commit 2f47b7b

Please sign in to comment.