fix THP migration for CMA allocations
Subsystems (3)
cover fix THP migration for CMA allocations
Transparent huge pages are allocated with __GFP_MOVABLE, and can end up in CMA memory blocks. Transparent huge pages also have most of the infrastructure in place to allow migration.
However, a few pieces were missing, causing THP migration to fail when attempting to use CMA to allocate 1GB hugepages.
With these patches in place, THP migration from CMA blocks seems to work, both for anonymous THPs and for tmpfs/shmem THPs.
Rik van Riel (2):
mm,compaction,cma: add alloc_contig flag to compact_control mm,thp,compaction,cma: allow THP migration for CMA allocations
mm/compaction.c | 16 +++++++++------- mm/internal.h | 1 + mm/page_alloc.c | 7 +++++-- 3 files changed, 15 insertions(+), 9 deletions(-)
Signature · 1 line
2.24.1
PATCH 1/2 mm,compaction,cma: add alloc_contig flag to compact_control
Add information to struct compact_control to indicate that the allocator would really like to clear out this specific part of memory, used by for example CMA.
--- mm/internal.h | 1 + mm/page_alloc.c | 1 + 2 files changed, 2 insertions(+)
mm/internal.h +1 -0
@@ -206,6 +206,7 @@ struct compact_control {bool whole_zone;/* Whole zone should/has been scanned */ bool contended;/* Signal lock or sched contention */ bool rescan;/* Rescanning the same pageblock */ +bool alloc_contig;/* alloc_contig_range allocation */ };/*
mm/page_alloc.c +1 -0
@@ -8402,6 +8402,7 @@ int alloc_contig_range(unsigned long start, unsigned long end, .ignore_skip_hint = true, .no_set_skip_hint = true, .gfp_mask = current_gfp_context(gfp_mask), + .alloc_contig = true, };INIT_LIST_HEAD (&cc.migratepages);
Signature · 1 line
2.24.1
PATCH 2/2 mm,thp,compaction,cma: allow THP migration for CMA allocations
In reply to Rik van RielThe code to implement THP migrations already exists, and the code for CMA to clear out a region of memory already exists.
Only a few small tweaks are needed to allow CMA to move THP memory when attempting an allocation from alloc_contig_range.
With these changes, migrating THPs from a CMA area works when allocating a 1GB hugepage from CMA memory.
--- mm/compaction.c | 16 +++++++++------- mm/page_alloc.c | 6 ++++-- 2 files changed, 13 insertions(+), 9 deletions(-)
mm/compaction.c +9 -7
@@ -894,12 +894,12 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,/* * Regardless of being on LRU, compound pages such as THP and -* hugetlbfs are not to be compacted. We can potentially save -* a lot of iterations if we skip them at once. The check is -* racy, but we can consider only valid values and the only -* danger is skipping too much. +* hugetlbfs are not to be compacted most of the time. We can +* potentially save a lot of iterations if we skip them at +* once. The check is racy, but we can consider only valid +* values and the only danger is skipping too much. */ -if (PageCompound (page)) { +if (PageCompound (page) && !cc->alloc_contig) {const unsigned int order =compound_order (page);if (likely (order <MAX_ORDER ))
@@ -969,7 +969,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,* and it's on LRU. It can only be a THP so the order * is safe to read and it's 0 for tail pages. */ -if (unlikely (PageCompound (page))) { +if (unlikely (PageCompound (page) && !cc->alloc_contig)) { low_pfn +=compound_nr (page) -1 ; goto isolate_fail; }
@@ -981,7 +981,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,if (__isolate_lru_page (page, isolate_mode) !=0 ) goto isolate_fail; -VM_BUG_ON_PAGE (PageCompound (page), page); +/* The whole page is taken off the LRU; skip the tail pages. */ +if (PageCompound (page)) + low_pfn +=compound_nr (page) -1 ;/* Successfully isolated */ del_page_from_lru_list (page, lruvec,page_lru (page));
mm/page_alloc.c +4 -2
@@ -8253,14 +8253,16 @@ struct page *has_unmovable_pages(struct zone *zone, struct page *page,/* * Hugepages are not in LRU lists, but they're movable. +* THPs are on the LRU, but need to be counted as #small pages. * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ -if (PageHuge (page)) { +if (PageTransHuge (page)) {struct page *head =compound_head (page);unsigned int skip_pages; -if (!hugepage_migration_supported (page_hstate (head))) +if (PageHuge (page) && + !hugepage_migration_supported (page_hstate (head)))return page; skip_pages =compound_nr (head) - (page - head);
Signature · 1 line
2.24.1
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 22 lines · Rik van Riel
The code to implement THP migrations already exists, and the code for CMA to clear out a region of memory already exists.
Only a few small tweaks are needed to allow CMA to move THP memory when attempting an allocation from
alloc_contig_range.With these changes, migrating THPs from a CMA area works when allocating a 1GB hugepage from CMA memory.
Signed-off-by Rik van Riel <[email protected]>--- mm/compaction.c | 16 +++++++++------- mm/page_alloc.c | 6 ++++-- 2 files changed, 13 insertions(+), 9 deletions(-)mm/compaction.c +0
-0@@ -894,12 +894,12 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,/*
mm/compaction.c isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, · line 901
Quoted 10 lines
mm/compaction.c +4
-4* Regardless of being on LRU, compound pages such as THP and -* hugetlbfs are not to be compacted. We can potentially save -* a lot of iterations if we skip them at once. The check is -* racy, but we can consider only valid values and the only -* danger is skipping too much. +* hugetlbfs are not to be compacted most of the time. We can +* potentially save a lot of iterations if we skip them at +* once. The check is racy, but we can consider only valid +* values and the only danger is skipping too much. */
Maybe add “we do want to move them when allocating contiguous memory using CMA” to help people understand the context of using cc->alloc_contig?
Quoted 50 lines
mm/compaction.c +5
-3-if (PageCompound (page)) { +if (PageCompound (page) && !cc->alloc_contig) {const unsigned int order =compound_order (page);if (likely (order <MAX_ORDER ))@@ -969,7 +969,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,* and it's on LRU. It can only be a THP so the order * is safe to read and it's 0 for tail pages. */ -if (unlikely (PageCompound (page))) { +if (unlikely (PageCompound (page) && !cc->alloc_contig)) { low_pfn +=compound_nr (page) -1 ; goto isolate_fail; }@@ -981,7 +981,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,if (__isolate_lru_page (page, isolate_mode) !=0 ) goto isolate_fail; -VM_BUG_ON_PAGE (PageCompound (page), page); +/* The whole page is taken off the LRU; skip the tail pages. */ +if (PageCompound (page)) + low_pfn +=compound_nr (page) -1 ;/* Successfully isolated */ del_page_from_lru_list (page, lruvec,page_lru (page));mm/page_alloc.c +4
-2@@ -8253,14 +8253,16 @@ struct page *has_unmovable_pages(struct zone *zone, struct page *page,/* * Hugepages are not in LRU lists, but they're movable. +* THPs are on the LRU, but need to be counted as #small pages. * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ -if (PageHuge (page)) { +if (PageTransHuge (page)) {struct page *head =compound_head (page);unsigned int skip_pages; -if (!hugepage_migration_supported (page_hstate (head))) +if (PageHuge (page) && + !hugepage_migration_supported (page_hstate (head)))return page; skip_pages =compound_nr (head) - (page - head);Signature · 1 line
2.24.1
Everything else looks good to me.
--
Best Regards,
Yan Zi
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 20 lines · Zi Yan
Quoted 18 lines · Rik van Riel
+++
b/mm/compaction.c(quoted diff) +0
-0@@ -894,12 +894,12 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, /* * Regardless of being on LRU, compound pages such asTHP and
(quoted diff) +0
-1- * hugetlbfs are not to be compacted. We canpotentially save
(quoted diff) +0
-1- * a lot of iterations if we skip them at once. Thecheck is
(quoted diff) +0
-1- * racy, but we can consider only valid values and theonly
(quoted diff) +1
-1- * danger is skipping too much. + * hugetlbfs are not to be compacted most of the time.We can
(quoted diff) +1
-0+ * potentially save a lot of iterations if we skip themat
Quoted 8 lines
mm/compaction.c isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, · line 901
Quoted 4 lines
(quoted diff) +1
-0+ * once. The check is racy, but we can consider onlyvalid
mm/compaction.c +1
-0+* values and the only danger is skipping too much. */ Maybe add “we do want to move them when allocating contiguous memory using CMA” to help
people understand the context of usingcc->alloc_contig?
I can certainly do that.
I'll wait for feedback from other people to see if more changes are wanted, and plan to post v2 by Tuesday or so :)
Signature · 1 line
All Rights Reversed.
Re: [PATCH 1/2] mm,compaction,cma: add alloc_contig flag to compact_control
In reply to Rik van RielQuoted 5 lines · Rik van Riel
Add information to struct
compact_controlto indicate that the allocator would really like to clear out this specific part of memory, used by for example CMA.Signed-off-by Rik van Riel <[email protected]>
Could have been just squashed into patch 2, but no strong feelings.
Quoted 30 lines
--- mm/internal.h | 1 + mm/page_alloc.c | 1 + 2 files changed, 2 insertions(+)mm/internal.h +1
-0@@ -206,6 +206,7 @@ struct compact_control {bool whole_zone;/* Whole zone should/has been scanned */ bool contended;/* Signal lock or sched contention */ bool rescan;/* Rescanning the same pageblock */ +bool alloc_contig;/* alloc_contig_range allocation */ };/* mm/page_alloc.c +1
-0@@ -8402,6 +8402,7 @@ int alloc_contig_range(unsigned long start, unsigned long end, .ignore_skip_hint = true, .no_set_skip_hint = true, .gfp_mask = current_gfp_context(gfp_mask), + .alloc_contig = true, };INIT_LIST_HEAD (&cc.migratepages);
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
In reply to Rik van Rielmm/compaction.c isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, · line 989
Quoted 11 lines · Rik van Riel
mm/compaction.c +3
-1@@ -981,7 +981,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,if (__isolate_lru_page (page, isolate_mode) !=0 ) goto isolate_fail; -VM_BUG_ON_PAGE (PageCompound (page), page); +/* The whole page is taken off the LRU; skip the tail pages. */ +if (PageCompound (page)) + low_pfn +=compound_nr (page) -1 ;/* Successfully isolated */ del_page_from_lru_list (page, lruvec,page_lru (page));
This continues by:inc_node_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page));
I think it now needs to use mod_node_page_state() with hpage_nr_pages(page) otherwise the counter will underflow after the migration?
Quoted 5 lines
mm/page_alloc.c +0
-0
mm/page_alloc.c struct page *has_unmovable_pages(struct zone *zone, struct page *page, · line 8260
Quoted 9 lines
mm/page_alloc.c +2
-1@@ -8253,14 +8253,16 @@ struct page *has_unmovable_pages(struct zone *zone, struct page *page,/* * Hugepages are not in LRU lists, but they're movable. +* THPs are on the LRU, but need to be counted as #small pages. * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ -if (PageHuge (page)) { +if (PageTransHuge (page)) {
Hmm, PageTransHuge() has VM_BUG_ON() for tail pages, while this code is written so that it can encounter a tail page and skip the rest of the compound page properly. So I would be worried about this.
Also PageTransHuge() is basically just a PageHead() so for each non-hugetlbfs compound page this will assume it's a THP, while correctly it should reach the __PageMovable() || PageLRU(page) tests below.
So probably this should do something like.
if (PageHuge(page) || PageTransCompound(page)) {
...
if (PageHuge(page) && !hpage_migration_supported)) return page. if (!PageLRU(head) && !__PageMovable(head)) return page
...
Quoted 10 lines
mm/page_alloc.c +2
-1struct page *head =compound_head (page);unsigned int skip_pages; -if (!hugepage_migration_supported (page_hstate (head))) +if (PageHuge (page) && + !hugepage_migration_supported (page_hstate (head)))return page; skip_pages =compound_nr (head) - (page - head);
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 20 lines · Vlastimil Babka
mm/compaction.c isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, · line 989
Quoted 13 lines · Rik van Riel
mm/compaction.c +1
-1@@ -981,7 +981,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, if (__isolate_lru_page(page, isolate_mode) != 0) goto isolate_fail; -VM_BUG_ON_PAGE (PageCompound (page), page); +/* The whole page is taken off the LRU; skip the tail pages. */
mm/compaction.c +2
-0+if (PageCompound (page)) + low_pfn +=compound_nr (page) -1 ;/* Successfully isolated */ del_page_from_lru_list (page, lruvec,page_lru (page));This continues by:
inc_node_page_state(page,NR_ISOLATED_ANON+page_is_file_cache(page));
Quoted 3 lines
I think it now needs to use
mod_node_page_state()withhpage_nr_pages(page)otherwise the counter will underflow after the migration?
You are absolutely right. I have not observed the underflow, but the functions doing the decrementing use hpage_nr_pages, and I need to do that as well on the incrementing side.
Change made.
Quoted 7 lines
Quoted 7 lines
mm/page_alloc.c +0
-0@@ -8253,14 +8253,16 @@ struct page *has_unmovable_pages(struct zone *zone, struct page *page,
Quoted 14 lines
mm/page_alloc.c struct page *has_unmovable_pages(struct zone *zone, struct page *page, · line 8260
Quoted 9 lines
mm/page_alloc.c +2
-1/* * Hugepages are not in LRU lists, but they're movable. +* THPs are on the LRU, but need to be counted as #small pages. * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ -if (PageHuge (page)) { +if (PageTransHuge (page)) {Hmm,
PageTransHuge()hasVM_BUG_ON()for tail pages, while this code is
written so that it can encounter a tail page and skip the rest of the compound page properly. So I would be worried about this.
Good point, a CMA allocation could start partway into a compound page.
Quoted 10 lines
Also
PageTransHuge()is basically just aPageHead()so for each non-hugetlbfs compound page this will assume it's a THP, while correctly
it should reach the__PageMovable()|| PageLRU(page) tests below.So probably this should do something like.
if (PageHuge(page) || PageTransCompound(page)) {
...
if (PageHuge(page) && !hpage_migration_supported)) return page.
So far so good.
Quoted 1 line
if (!PageLRU(head) && !__PageMovable(head)) return page
I don't get this one, though. What about a THP that has not made it onto the LRU list yet for some reason?
I don't think anonymous pages are marked __PageMovable, are they? It looks like they only have the PAGE_MAPPING_ANON flag set, not the PAGE_MAPPING_MOVABLE one.
What am I missing?
Quoted 16 lines
...
Quoted 12 lines
mm/page_alloc.c +0
-1struct page *head =compound_head (page);unsigned int skip_pages; - if(!hugepage_migration_supported(page_hstate(head)))
mm/page_alloc.c +2
-0+if (PageHuge (page) && + !hugepage_migration_supported(page_hstate(head)))
mm/page_alloc.c +0
-0return page; skip_pages =compound_nr (head) - (page - head);
Signature · 1 line
All Rights Reversed.
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 11 lines · Rik van Riel
Quoted 10 lines
Also
PageTransHuge()is basically just aPageHead()so for each non-hugetlbfs compound page this will assume it's a THP, while correctly
it should reach the__PageMovable()|| PageLRU(page) tests below.So probably this should do something like.
if (PageHuge(page) || PageTransCompound(page)) {
...
if (PageHuge(page) && !hpage_migration_supported)) return page.
Quoted 6 lines
So far so good.
Quoted 1 line
if (!PageLRU(head) && !__PageMovable(head)) return pageI don't get this one, though. What about a THP that has not made it onto the LRU list yet for some reason?
Uh, is it any different from base pages which have to pass the same check? I guess the caller could do e.g. lru_add_drain_all() first.
Quoted 5 lines
I don't think anonymous pages are marked
__PageMovable, are they? It looks like they only have thePAGE_MAPPING_ANONflag set, not thePAGE_MAPPING_MOVABLEone.What am I missing?
My point is that we should not accept compound pages that are neither a migratable hugetlbfs page nor a THP, as movable. And your PageTransHuge() test and my PageTransCompound() is really just a test for all compound pages, not "hugetlbfs or THP only". I should have perhaps suggested PageCompound() instead of the PageTransCompound() wrapper, to make it more obvious.
So we should test non-hugetlbfs pages first whether they are the kind of compound pages that are migratable. THP's should pass this test by PageLRU(), other compound movable pages by __PageMovable(head).
Quoted 18 lines
Quoted 17 lines
...
Quoted 12 lines
mm/page_alloc.c +0
-1struct page *head =compound_head (page);unsigned int skip_pages; - if(!hugepage_migration_supported(page_hstate(head)))
(quoted diff) +2
-0+ if (PageHuge(page) && + !hugepage_migration_supported(page_hstate(head)))
mm/page_alloc.c +0
-0return page; skip_pages =compound_nr (head) - (page - head);
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 15 lines · Vlastimil Babka
Quoted 14 lines · Rik van Riel
Quoted 11 lines
Also
PageTransHuge()is basically just aPageHead()so for each non-hugetlbfs compound page this will assume it's a THP, while correctly
it should reach the__PageMovable()|| PageLRU(page) tests below.So probably this should do something like.
if (PageHuge(page) || PageTransCompound(page)) {
...
if (PageHuge(page) && !hpage_migration_supported)) return
page.So far so good.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
if (!PageLRU(head) && !__PageMovable(head)) return pageI don't get this one, though. What about a THP that has not made it onto the LRU list yet for some reason?
Uh, is it any different from base pages which have to pass the same check? I
guess the caller could do e.g.lru_add_drain_all()first.
You are right, it is not different.
As for lru_add_drain_all(), I wonder at what point that should happen?
It appears that the order in which things are done does not really provide a good moment:
1) decide to attempt allocating a range of memory
2) scan each page block for unmovable pages
3) if no unmovable pages are found, mark the page block MIGRATE_ISOLATE
I wonder if we should do things the opposite way, first marking the page block MIGRATE_ISOLATE (to prevent new allocations), then scanning it, and calling lru_add_drain_all if we encounter a page that looks like it could benefit from that.
If we still see unmovable pages after that, it is cheap enough to set the page block back to its previous state.
Quoted 9 lines
Quoted 5 lines
I don't think anonymous pages are marked
__PageMovable, are they? It looks like they only have thePAGE_MAPPING_ANONflag set, not thePAGE_MAPPING_MOVABLEone.What am I missing?
My point is that we should not accept compound pages that are neither a
migratable hugetlbfs page nor a THP, as movable.
I have merged your suggestions into my code base. Thank you for pointing out that 4kB pages have the exact same restrictions as THPs, and why.
I'll run some tests and will post v2 of the series soon.
Signature · 1 line
All Rights Reversed.
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 11 lines · Rik van Riel
Quoted 5 lines · Vlastimil Babka
On 2/25/20 7:44 PM, Rik van Riel wrote:
Uh, is it any different from base pages which have to pass the same check? I
guess the caller could do e.g.lru_add_drain_all()first.You are right, it is not different.
As for
lru_add_drain_all(), I wonder at what point that should happen?
Right now it seems to be done in alloc_contig_range(), but rather late.
Quoted 7 lines
It appears that the order in which things are done does not really provide a good moment:
1) decide to attempt allocating a range of memory
2) scan each page block for unmovable pages
3) if no unmovable pages are found, mark the page blockMIGRATE_ISOLATE
Quoted 8 lines
I wonder if we should do things the opposite way, first marking the page block
MIGRATE_ISOLATE(to prevent new allocations), then scanning it, and callinglru_add_drain_allif we encounter a page that looks like it could benefit from that.If we still see unmovable pages after that, it is cheap enough to set the page block back to its previous state.
Yeah seems like the whole has_unmovable_pages() thing isn't much useful here. It might prevent some unnecessary action like isolating something, then finding non-movable page and rolling back the isolation. But maybe it's not worth the savings, and also has_unmovable_pages() being false doesn't guarantee succeed in the actual isolate+migrate attempt. And if it can cause a false negative due to lru pages not drained, then it's actually worse than if it wasn't called at all.
Re: [PATCH 2/2] mm,thp,compaction,cma: allow THP migration for CMA allocations
Quoted 18 lines · Vlastimil Babka
Quoted 8 lines · Rik van Riel
It appears that the order in which things are done does not really provide a good moment:
1) decide to attempt allocating a range of memory
2) scan each page block for unmovable pages
3) if no unmovable pages are found, mark the page blockMIGRATE_ISOLATEQuoted 8 lines
I wonder if we should do things the opposite way, first marking the page block
MIGRATE_ISOLATE(to prevent new allocations), then scanning it, and callinglru_add_drain_allif we encounter a page that looks like it could benefit from that.If we still see unmovable pages after that, it is cheap enough to set the page block back to its previous state.
Quoted 12 lines
Yeah seems like the whole
has_unmovable_pages()thing isn't much useful
here. It might prevent some unnecessary action like isolating something,
then finding non-movable page and rolling back the isolation. But maybe
it's not worth the savings, and alsohas_unmovable_pages()being false
doesn't guarantee succeed in the actual isolate+migrate attempt. And if
it can cause a false negative due to lru pages not drained, then it's actually worse than if it wasn't called at all.
We'll experiment with that, and see how often it is an issue in practice.
If this aspect of the code needs improving, I suspect Roman and I will find it soon enough.
Signature · 1 line
All Rights Reversed.