Generating physically contiguous memory after page allocation
Subsystems (16)
Zi Yan
reviewercover Generating physically contiguous memory after page allocation
Hi all,
This patchset produces physically contiguous memory by moving in-use pages without allocating any new pages. It targets two scenarios that complements khugepaged use cases: 1) avoiding page reclaim and memory compaction when the system is under memory pressure because this patchset does not allocate any new pages, 2) generating pages larger than 2^MAX_ORDER without changing the buddy allocator.
To demonstrate its use, I add very basic 1GB THP support and enable promoting 512 2MB THPs to a 1GB THP in my patchset. Promoting 512 4KB pages to a 2MB THP is also implemented.
The patches are on top of v5.0-rc5. They are posted as part of my upcoming LSF/MM proposal.
Motivation
----
The goal of this patchset is to provide alternative way of generating physically contiguous memory and making it available as arbitrary sized large pages. This patchset generates physically contiguous memory/arbitrary size pages after pages are allocated by moving virtually-contiguous pages to become physically contiguous at any size, thus it does not require changes to memory allocators. On the other hand, it works only for moveable pages, so it also faces the same fragmentation issues as memory compaction, i.e., if non-moveable pages spread across the entire memory, this patchset can only generate contiguity between any two non-moveable pages.
Large pages and physically contiguous memory are important to devices, such as GPUs, FPGAs, NICs and RDMA controllers, because they can often achieve better performance when operating on large pages. The same can be said of CPU performance, of course, but there is an important difference: GPUs and high-throughput devices often take a more severe performance hit, in the event of a TLB miss and subsequent page table walks, as compared to a CPU. The effect is sufficiently large that such devices *really* want a highly reliable way to allocate large pages to minimize the number of potential TLB misses and the time spent on the induced page table walks.
Vendors (like Oracle, Mellanox, IBM, NVIDIA) are interested in generating physically contiguous memory beyond THP sizes and looking for solutions [1],[2],[3]. This patchset provides an alternative approach, compared to allocating physically contiguous memory at page allocation time, to generating physically contiguous memory after pages are allocated. This approach can avoid page reclaim and memory compaction, which happen during the process of page allocation, but still produces comparable physically contiguous memory.
In terms of THPs, it helps, but we are interested in even larger contiguous ranges (or page size support) to further reduce the address translation overheads. With this patchset, we can generate pages larger than PMD-level THPs without requiring MAX_ORDER changes in the buddy allocators.
Patch structure
----
The patchset I developed to generate physically contiguous memory/arbitrary sized pages merely moves pages around. There are three components in this patchset:
1) a new page migration mechanism, called exchange pages, that exchanges the content of two in-use pages instead of performing two back-to-back page migration. It saves on overheads and avoids page reclaim and memory compaction in the page allocation path, although it is not strictly required if enough free memory is available in the system.
2) a new mechanism that utilizes both page migration and exchange pages to produce physically contiguous memory/arbitrary sized pages without allocating any new pages, unlike what khugepaged does. It works on per-VMA basis, creating physically contiguous memory out of each VMA, which is virtually contiguous. A simple range tree is used to ensure no two VMAs are overlapping with each other in the physical address space.
3) a use case of the new physically contiguous memory producing mechanism that generates 1GB THPs by migrating and exchanging pages and promoting 512 contiguous 2MB THPs to a 1GB THP, although even larger physically contiguous memory ranges can be generated. The 1GB THP implement is very basic, which can handle 1GB THP faults when buddy allocator is modified to allocate 1GB pages, support 1GB THP split to 2MB THP and in-place promotion from 2MB THP to 1GB THP, and PMD/PTE-mapped 1GB THP. These are not fully tested.
[1] https://lwn.net/Articles/736170/
[2] https://lwn.net/Articles/753167/
[3] https://blogs.nvidia.com/blog/2018/06/08/worlds-fastest-exascale-ai-supercomputer-summit/
Zi Yan (31):
mm: migrate: Add exchange_pages to exchange two lists of pages.
mm: migrate: Add THP exchange support.
mm: migrate: Add tmpfs exchange support.
mm: add mem_defrag functionality.
mem_defrag: split a THP if either src or dst is THP only.
mm: Make MAX_ORDER configurable in Kconfig for buddy allocator.
mm: deallocate pages with order > MAX_ORDER.
mm: add pagechain container for storing multiple pages.
mm: thp: 1GB anonymous page implementation.
mm: proc: add 1GB THP kpageflag.
mm: debug: print compound page order in dump_page().
mm: stats: Separate PMD THP and PUD THP stats.
mm: thp: 1GB THP copy on write implementation.
mm: thp: handling 1GB THP reference bit.
mm: thp: add 1GB THP split_huge_pud_page() function.
mm: thp: check compound_mapcount of PMD-mapped PUD THPs at free time.
mm: thp: split properly PMD-mapped PUD THP to PTE-mapped PUD THP.
mm: page_vma_walk: teach it about PMD-mapped PUD THP.
mm: thp: 1GB THP support in try_to_unmap().
mm: thp: split 1GB THPs at page reclaim.
mm: thp: 1GB zero page shrinker.
mm: thp: 1GB THP follow_p*d_page() support.
mm: support 1GB THP pagemap support.
sysctl: add an option to only print the head page virtual address.
mm: thp: add a knob to enable/disable 1GB THPs.
mm: thp: promote PTE-mapped THP to PMD-mapped THP.
mm: thp: promote PMD-mapped PUD pages to PUD-mapped PUD pages.
mm: vmstats: add page promotion stats.
mm: madvise: add madvise options to split PMD and PUD THPs.
mm: mem_defrag: thp: PMD THP and PUD THP in-place promotion support.
sysctl: toggle to promote PUD-mapped 1GB THP or not.
arch/x86/Kconfig | 15 + arch/x86/entry/syscalls/syscall_64.tbl | 1 + arch/x86/include/asm/pgalloc.h | 69 + arch/x86/include/asm/pgtable.h | 20 + arch/x86/include/asm/sparsemem.h | 4 +- arch/x86/mm/pgtable.c | 38 + drivers/base/node.c | 3 + fs/exec.c | 4 + fs/proc/meminfo.c | 2 + fs/proc/page.c | 2 + fs/proc/task_mmu.c | 47 +- include/asm-generic/pgtable.h | 110 + include/linux/huge_mm.h | 78 +- include/linux/khugepaged.h | 1 + include/linux/ksm.h | 5 + include/linux/mem_defrag.h | 60 + include/linux/memcontrol.h | 5 + include/linux/mm.h | 34 + include/linux/mm_types.h | 5 + include/linux/mmu_notifier.h | 13 + include/linux/mmzone.h | 1 + include/linux/page-flags.h | 79 +- include/linux/pagechain.h | 73 + include/linux/rmap.h | 10 +- include/linux/sched/coredump.h | 4 + include/linux/swap.h | 2 + include/linux/syscalls.h | 3 + include/linux/vm_event_item.h | 33 + include/uapi/asm-generic/mman-common.h | 15 + include/uapi/linux/kernel-page-flags.h | 2 + kernel/events/uprobes.c | 4 +- kernel/fork.c | 14 + kernel/sysctl.c | 101 +- mm/Makefile | 2 + mm/compaction.c | 17 +- mm/debug.c | 8 +- mm/exchange.c | 878 +++++++ mm/filemap.c | 8 + mm/gup.c | 60 +- mm/huge_memory.c | 3360 ++++++++++++++++++++---- mm/hugetlb.c | 4 +- mm/internal.h | 46 + mm/khugepaged.c | 7 +- mm/ksm.c | 39 +- mm/madvise.c | 121 + mm/mem_defrag.c | 1941 ++++++++++++++ mm/memcontrol.c | 13 + mm/memory.c | 55 +- mm/migrate.c | 14 +- mm/mmap.c | 29 + mm/page_alloc.c | 108 +- mm/page_vma_mapped.c | 129 +- mm/pgtable-generic.c | 78 +- mm/rmap.c | 283 +- mm/swap.c | 38 + mm/swap_slots.c | 2 + mm/swapfile.c | 4 +- mm/userfaultfd.c | 2 +- mm/util.c | 7 + mm/vmscan.c | 55 +- mm/vmstat.c | 32 + 61 files changed, 7452 insertions(+), 745 deletions(-) create mode 100644 include/linux/mem_defrag.h create mode 100644 include/linux/pagechain.h create mode 100644 mm/exchange.c create mode 100644 mm/mem_defrag.c
--
2.20.1
Zi Yan
reviewerRFC PATCH 1/31 mm: migrate: Add exchange_pages to exchange two lists of pages.
In stead of using two migrate_pages(), a single exchange_pages() would be sufficient and without allocating new pages.
--- include/linux/ksm.h | 5 + mm/Makefile | 1 + mm/exchange.c | 846 ++++++++++++++++++++++++++++++++++++++++++++ mm/internal.h | 6 + mm/ksm.c | 35 ++ mm/migrate.c | 4 +- 6 files changed, 895 insertions(+), 2 deletions(-) create mode 100644 mm/exchange.c
include/linux/ksm.h +5 -0
@@ -53,6 +53,7 @@ struct page *ksm_might_need_to_copy(struct page *page,void rmap_walk_ksm (struct page *page,struct rmap_walk_control *rwc);void ksm_migrate_page (struct page *newpage,struct page *oldpage); +void ksm_exchange_page (struct page *to_page,struct page *from_page);#else /* !CONFIG_KSM */
@@ -86,6 +87,10 @@ static inline void rmap_walk_ksm(struct page *page,static inline void ksm_migrate_page (struct page *newpage,struct page *oldpage) { } +static inline void ksm_exchange_page (struct page *to_page, +struct page *from_page) +{ +}#endif /* CONFIG_MMU */ #endif /* !CONFIG_KSM */
mm/Makefile +1 -0
@@ -43,6 +43,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ obj-y += init-mm.o obj-y += memblock.o +obj-y += exchange.o ifdef CONFIG_MMU obj-$(CONFIG_ADVISE_SYSCALLS) += madvise.o
mm/exchange.c new file +846 -0
846 lines not on this page · raw message
mm/internal.h +6 -0
@@ -543,4 +543,10 @@ static inline bool is_migrate_highatomic_page(struct page *page)void setup_zone_pageset (struct zone *zone);extern struct page *alloc_new_node_page (struct page *page,unsigned long node); + +bool buffer_migrate_lock_buffers (struct buffer_head *head, +enum migrate_mode mode); +int writeout (struct address_space *mapping,struct page *page); +extern int exchange_two_pages (struct page *page1,struct page *page2); +#endif /* __MM_INTERNAL_H */
mm/ksm.c +35 -0
@@ -2665,6 +2665,41 @@ void ksm_migrate_page(struct page *newpage, struct page *oldpage) set_page_stable_node(oldpage, NULL); } } + +void ksm_exchange_page (struct page *to_page,struct page *from_page) +{ +struct stable_node *to_stable_node, *from_stable_node; + +VM_BUG_ON_PAGE (!PageLocked (to_page), to_page); +VM_BUG_ON_PAGE (!PageLocked (from_page), from_page); + + to_stable_node =page_stable_node (to_page); + from_stable_node =page_stable_node (from_page); +if (to_stable_node) { +VM_BUG_ON_PAGE (to_stable_node->kpfn !=page_to_pfn (from_page), + from_page); + to_stable_node->kpfn =page_to_pfn (to_page); +/* +* newpage->mapping was set in advance; now we need smp_wmb() +* to make sure that the new stable_node->kpfn is visible +* to get_ksm_page() before it can see that oldpage->mapping +* has gone stale (or that PageSwapCache has been cleared). +*/ +smp_wmb (); + } +if (from_stable_node) { +VM_BUG_ON_PAGE (from_stable_node->kpfn !=page_to_pfn (to_page), + to_page); + from_stable_node->kpfn =page_to_pfn (from_page); +/* +* newpage->mapping was set in advance; now we need smp_wmb() +* to make sure that the new stable_node->kpfn is visible +* to get_ksm_page() before it can see that oldpage->mapping +* has gone stale (or that PageSwapCache has been cleared). +*/ +smp_wmb (); + } +}#endif /* CONFIG_MIGRATION */ #ifdef CONFIG_MEMORY_HOTREMOVE
mm/migrate.c +2 -2
@@ -701,7 +701,7 @@ EXPORT_SYMBOL(migrate_page);#ifdef CONFIG_BLOCK /* Returns true if all buffers are successfully locked */ -static bool buffer_migrate_lock_buffers (struct buffer_head *head, +bool buffer_migrate_lock_buffers (struct buffer_head *head,enum migrate_mode mode) {struct buffer_head *bh = head;
@@ -849,7 +849,7 @@ int buffer_migrate_page_norefs(struct address_space *mapping,/* * Writeback a page to clean the dirty state */ -static int writeout (struct address_space *mapping,struct page *page) +int writeout (struct address_space *mapping,struct page *page) {struct writeback_control wbc = { .sync_mode =WB_SYNC_NONE ,
Signature · 1 line
2.20.1
Zi Yan
reviewerRFC PATCH 2/31 mm: migrate: Add THP exchange support.
In reply to Zi YanAdd support for exchange between two THPs.
--- mm/exchange.c | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-)
mm/exchange.c +38 -9
@@ -48,7 +48,8 @@ struct page_flags { unsigned int page_swapcache:1; unsigned int page_writeback:1; unsigned int page_private:1; - unsigned int __pad:3; + unsigned int page_doublemap:1; + unsigned int __pad:2; };
@@ -125,20 +126,23 @@ static void exchange_huge_page(struct page *dst, struct page *src)static void exchange_page_flags (struct page *to_page,struct page *from_page) {int from_cpupid, to_cpupid; -struct page_flags from_page_flags, to_page_flags; +struct page_flags from_page_flags = {0 }, to_page_flags = {0 };struct mem_cgroup *to_memcg =page_memcg (to_page), *from_memcg =page_memcg (from_page); from_cpupid =page_cpupid_xchg_last (from_page,-1 ); - from_page_flags.page_error =TestClearPageError (from_page); + from_page_flags.page_error =PageError (from_page); +if (from_page_flags.page_error) +ClearPageError (from_page); from_page_flags.page_referenced =TestClearPageReferenced (from_page); from_page_flags.page_uptodate =PageUptodate (from_page);ClearPageUptodate (from_page); from_page_flags.page_active =TestClearPageActive (from_page); from_page_flags.page_unevictable =TestClearPageUnevictable (from_page); from_page_flags.page_checked =PageChecked (from_page); -ClearPageChecked (from_page); +if (from_page_flags.page_checked) +ClearPageChecked (from_page); from_page_flags.page_mappedtodisk =PageMappedToDisk (from_page);ClearPageMappedToDisk (from_page); from_page_flags.page_dirty =PageDirty (from_page);
@@ -148,18 +152,22 @@ static void exchange_page_flags(struct page *to_page, struct page *from_page)clear_page_idle (from_page); from_page_flags.page_swapcache =PageSwapCache (from_page); from_page_flags.page_writeback =test_clear_page_writeback (from_page); + from_page_flags.page_doublemap =PageDoubleMap (from_page); to_cpupid =page_cpupid_xchg_last (to_page,-1 ); - to_page_flags.page_error =TestClearPageError (to_page); + to_page_flags.page_error =PageError (to_page); +if (to_page_flags.page_error) +ClearPageError (to_page); to_page_flags.page_referenced =TestClearPageReferenced (to_page); to_page_flags.page_uptodate =PageUptodate (to_page);ClearPageUptodate (to_page); to_page_flags.page_active =TestClearPageActive (to_page); to_page_flags.page_unevictable =TestClearPageUnevictable (to_page); to_page_flags.page_checked =PageChecked (to_page); -ClearPageChecked (to_page); +if (to_page_flags.page_checked) +ClearPageChecked (to_page); to_page_flags.page_mappedtodisk =PageMappedToDisk (to_page);ClearPageMappedToDisk (to_page); to_page_flags.page_dirty =PageDirty (to_page);
@@ -169,6 +177,7 @@ static void exchange_page_flags(struct page *to_page, struct page *from_page)clear_page_idle (to_page); to_page_flags.page_swapcache =PageSwapCache (to_page); to_page_flags.page_writeback =test_clear_page_writeback (to_page); + to_page_flags.page_doublemap =PageDoubleMap (to_page);/* set to_page */ if (from_page_flags.page_error)
@@ -195,6 +204,8 @@ static void exchange_page_flags(struct page *to_page, struct page *from_page)set_page_young (to_page);if (from_page_flags.page_is_idle)set_page_idle (to_page); +if (from_page_flags.page_doublemap) +SetPageDoubleMap (to_page);/* set from_page */ if (to_page_flags.page_error)
@@ -221,6 +232,8 @@ static void exchange_page_flags(struct page *to_page, struct page *from_page)set_page_young (from_page);if (to_page_flags.page_is_idle)set_page_idle (from_page); +if (to_page_flags.page_doublemap) +SetPageDoubleMap (from_page);/* * Copy NUMA information to the new page, to prevent over-eager
@@ -280,6 +293,7 @@ static int exchange_page_move_mapping(struct address_space *to_mapping,VM_BUG_ON_PAGE (to_mapping !=page_mapping (to_page), to_page);VM_BUG_ON_PAGE (from_mapping !=page_mapping (from_page), from_page); +VM_BUG_ON (PageCompound (from_page) !=PageCompound (to_page));if (!to_mapping) {/* Anonymous page without mapping */
@@ -600,7 +614,6 @@ static int unmap_and_exchange(struct page *from_page, to_mapping = to_page->mapping; from_index = from_page->index; to_index = to_page->index; -/* * Corner case handling: * 1. When a new swap-cache page is read into, it is added to the LRU
@@ -691,6 +704,23 @@ static int unmap_and_exchange(struct page *from_page,return rc; } +static bool can_be_exchanged (struct page *from,struct page *to) +{ +if (PageCompound (from) !=PageCompound (to)) +return false; + +if (PageHuge (from) !=PageHuge (to)) +return false; + +if (PageHuge (from) ||PageHuge (to)) +return false; + +if (compound_order (from) !=compound_order (to)) +return false; + +return true; +} +/* * Exchange pages in the exchange_list *
@@ -751,9 +781,8 @@ static int exchange_pages(struct list_head *exchange_list,continue ; } -/* TODO: compound page not supported */ /* to_page can be file-backed page */ -if (PageCompound (from_page) || +if (!can_be_exchanged (from_page, to_page) ||page_mapping (from_page) ) { ++failed;
Signature · 1 line
2.20.1
Zi Yan
reviewerRFC PATCH 3/31 mm: migrate: Add tmpfs exchange support.
In reply to Zi Yantmpfs uses the same migrate routine as anonymous pages, enabling exchange pages for it is easy.
--- mm/exchange.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
mm/exchange.c +4 -1
@@ -466,7 +466,10 @@ static int exchange_from_to_pages(struct page *to_page, struct page *from_page, rc =exchange_page_move_mapping (to_page_mapping, from_page_mapping, to_page, from_page,NULL ,NULL , mode,0 ,0 ); }else { -if (to_page_mapping->a_ops->migratepage == buffer_migrate_page) { +/* shmem */ +if (to_page_mapping->a_ops->migratepage == migrate_page) + goto exchange_mappings; +else if (to_page_mapping->a_ops->migratepage == buffer_migrate_page) {if (!page_has_buffers (to_page)) goto exchange_mappings;
Signature · 1 line
2.20.1
Zi Yan
reviewerRFC PATCH 4/31 mm: add mem_defrag functionality.
In reply to Zi YanCreate contiguous physical memory regions by migrating/exchanging pages.
1. It scans all VMAs in one process and determines an anchor pair (VPN, PFN) for each VMA.
2. Then, it migrates/exchanges pages in each VMA to make them aligned with the anchor pair.
At the end of day, a physically contiguous region should be created for each VMA in the physical address range [PFN, PFN + VMA size).
--- arch/x86/entry/syscalls/syscall_64.tbl | 1 + fs/exec.c | 4 + include/linux/mem_defrag.h | 60 + include/linux/mm.h | 12 + include/linux/mm_types.h | 4 + include/linux/sched/coredump.h | 3 + include/linux/syscalls.h | 3 + include/linux/vm_event_item.h | 23 + include/uapi/asm-generic/mman-common.h | 3 + kernel/fork.c | 9 + kernel/sysctl.c | 79 +- mm/Makefile | 1 + mm/compaction.c | 17 +- mm/huge_memory.c | 4 + mm/internal.h | 28 + mm/khugepaged.c | 1 + mm/madvise.c | 15 + mm/mem_defrag.c | 1782 ++++++++++++++++++++++++ mm/memory.c | 7 + mm/mmap.c | 29 + mm/page_alloc.c | 4 +- mm/vmstat.c | 21 + 22 files changed, 2096 insertions(+), 14 deletions(-) create mode 100644 include/linux/mem_defrag.h create mode 100644 mm/mem_defrag.c
arch/x86/entry/syscalls/syscall_64.tbl +1 -0
@@ -343,6 +343,7 @@ 332 common statx __x64_sys_statx 333 common io_pgetevents __x64_sys_io_pgetevents 334 common rseq __x64_sys_rseq +335 common scan_process_memory __x64_sys_scan_process_memory # # x32-specific system call numbers start at 512 to avoid cache impact
fs/exec.c +4 -0
@@ -1010,7 +1010,11 @@ static int exec_mmap(struct mm_struct *mm) {struct task_struct *tsk;struct mm_struct *old_mm, *active_mm; +int move_mem_defrag = current->mm ? +test_bit (MMF_VM_MEM_DEFRAG_ALL , ¤t->mm->flags):0 ; +if (move_mem_defrag && mm) +set_bit (MMF_VM_MEM_DEFRAG_ALL , &mm->flags);/* Notify parent that we're no longer interested in the old VM */ tsk = current; old_mm = current->mm;
include/linux/mem_defrag.h new file +60 -0
60 lines not on this page · raw message
include/linux/mm.h +12 -0
@@ -251,13 +251,20 @@ extern unsigned int kobjsize(const void *objp);#define VM_HIGH_ARCH_BIT_2 34/* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_BIT_3 35/* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_BIT_4 36/* bit only usable on 64-bit architectures */ +#define VM_HIGH_ARCH_BIT_5 37/* bit only usable on 64-bit architectures */ +#define VM_HIGH_ARCH_BIT_6 38/* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_0 BIT(VM_HIGH_ARCH_BIT_0)#define VM_HIGH_ARCH_1 BIT(VM_HIGH_ARCH_BIT_1)#define VM_HIGH_ARCH_2 BIT(VM_HIGH_ARCH_BIT_2)#define VM_HIGH_ARCH_3 BIT(VM_HIGH_ARCH_BIT_3)#define VM_HIGH_ARCH_4 BIT(VM_HIGH_ARCH_BIT_4) +#define VM_HIGH_ARCH_5 BIT(VM_HIGH_ARCH_BIT_5) +#define VM_HIGH_ARCH_6 BIT(VM_HIGH_ARCH_BIT_6)#endif /* CONFIG_ARCH_USES_HIGH_VMA_FLAGS */ +#define VM_MEMDEFRAG VM_HIGH_ARCH_5/* memory defrag */ +#define VM_NOMEMDEFRAG VM_HIGH_ARCH_6/* no memory defrag */ +#ifdef CONFIG_ARCH_HAS_PKEYS # define VM_PKEY_SHIFT VM_HIGH_ARCH_BIT_0# define VM_PKEY_BIT0 VM_HIGH_ARCH_0/* A protection key is a 4-bit value */
@@ -487,6 +494,9 @@ static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm) vma->vm_mm = mm; vma->vm_ops = &dummy_vm_ops;INIT_LIST_HEAD (&vma->anon_vma_chain); + vma->anchor_page_rb =RB_ROOT_CACHED ; + vma->vma_create_jiffies = jiffies; + vma->vma_defrag_jiffies =0 ; }static inline void vma_set_anonymous (struct vm_area_struct *vma)
@@ -2837,6 +2847,8 @@ static inline bool debug_guardpage_enabled(void) { return false; }static inline bool page_is_guard (struct page *page) {return false; }#endif /* CONFIG_DEBUG_PAGEALLOC */ +void free_anchor_pages (struct vm_area_struct *vma); +#if MAX_NUMNODES >1 void __init setup_nr_node_ids (void );#else
include/linux/mm_types.h +4 -0
@@ -328,6 +328,10 @@ struct vm_area_struct {struct mempolicy *vm_policy;/* NUMA policy for the VMA */ #endif struct vm_userfaultfd_ctx vm_userfaultfd_ctx; +struct rb_root_cached anchor_page_rb; +unsigned long vma_create_jiffies;/* life time of the vma */ +unsigned long vma_modify_jiffies;/* being modified time of the vma */ +unsigned long vma_defrag_jiffies;/* being defragged time of the vma */ } __randomize_layout;struct core_thread {
include/linux/sched/coredump.h +3 -0
@@ -76,5 +76,8 @@ static inline int get_dumpable(struct mm_struct *mm)#define MMF_INIT_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\ MMF_DISABLE_THP_MASK) +#define MMF_VM_MEM_DEFRAG 26/* set mm is added to do mem defrag */ +#define MMF_VM_MEM_DEFRAG_ALL 27/* all vmas in the mm will be mem defrag */ +#endif /* _LINUX_SCHED_COREDUMP_H */
include/linux/syscalls.h +3 -0
@@ -926,6 +926,8 @@ asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, unsigned mask, struct statx __user *buffer);asmlinkage long sys_rseq (struct rseq __user *rseq,uint32_t rseq_len,int flags,uint32_t sig); +asmlinkage long sys_scan_process_memory (pid_t pid,char __user *out_buf, +int buf_len,int action);/* * Architecture-specific system calls
@@ -1315,4 +1317,5 @@ static inline unsigned int ksys_personality(unsigned int personality) return old; } +#endif
include/linux/vm_event_item.h +23 -0
@@ -109,6 +109,29 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,#ifdef CONFIG_SWAP SWAP_RA ,SWAP_RA_HIT , +#endif +/* MEM_DEFRAG STATS */ +MEM_DEFRAG_DEFRAG_NUM , +MEM_DEFRAG_SCAN_NUM , +MEM_DEFRAG_DST_FREE_PAGES , +MEM_DEFRAG_DST_ANON_PAGES , +MEM_DEFRAG_DST_FILE_PAGES , +MEM_DEFRAG_DST_NONLRU_PAGES , +MEM_DEFRAG_DST_FREE_PAGES_FAILED , +MEM_DEFRAG_DST_FREE_PAGES_OVERFLOW_FAILED , +MEM_DEFRAG_DST_ANON_PAGES_FAILED , +MEM_DEFRAG_DST_FILE_PAGES_FAILED , +MEM_DEFRAG_DST_NONLRU_PAGES_FAILED , +MEM_DEFRAG_SRC_ANON_PAGES_FAILED , +MEM_DEFRAG_SRC_COMP_PAGES_FAILED , +MEM_DEFRAG_DST_SPLIT_HUGEPAGES , +#ifdef CONFIG_COMPACTION +/* memory compaction */ +COMPACT_MIGRATE_PAGES , +#endif +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +/* thp collapse */ +THP_COLLAPSE_MIGRATE_PAGES , #endifNR_VM_EVENT_ITEMS };
include/uapi/asm-generic/mman-common.h +3 -0
@@ -66,6 +66,9 @@#define MADV_WIPEONFORK 18/* Zero memory on fork, child only */ #define MADV_KEEPONFORK 19/* Undo MADV_WIPEONFORK */ +#define MADV_MEMDEFRAG 20/* Worth backing with hugepages */ +#define MADV_NOMEMDEFRAG 21/* Not worth backing with hugepages */ +/* compatibility flags */ #define MAP_FILE 0
kernel/fork.c +9 -0
@@ -92,6 +92,7 @@#include <linux/livepatch.h> #include <linux/thread_info.h> #include <linux/stackleak.h> +#include <linux/mem_defrag.h> #include <asm/pgtable.h> #include <asm/pgalloc.h>
@@ -343,12 +344,16 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)if (new) { *new = *orig;INIT_LIST_HEAD (&new->anon_vma_chain); + new->anchor_page_rb =RB_ROOT_CACHED ; + new->vma_create_jiffies = jiffies; + new->vma_defrag_jiffies =0 ; }return new; }void vm_area_free (struct vm_area_struct *vma) { +free_anchor_pages (vma);kmem_cache_free (vm_area_cachep, vma); }
@@ -496,6 +501,9 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,if (retval) goto out; retval =khugepaged_fork (mm, oldmm); +if (retval) + goto out; + retval =kmem_defragd_fork (mm, oldmm);if (retval) goto out;
@@ -1044,6 +1052,7 @@ static inline void __mmput(struct mm_struct *mm)exit_aio (mm);ksm_exit (mm);khugepaged_exit (mm);/* must run before exit_mmap */ +kmem_defragd_exit (mm);exit_mmap (mm);mm_put_huge_zero_page (mm);set_mm_exe_file (mm,NULL );
kernel/sysctl.c +69 -10
137 lines not on this page · raw message
mm/Makefile +1 -0
@@ -44,6 +44,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ obj-y += init-mm.o obj-y += memblock.o obj-y += exchange.o +obj-y += mem_defrag.o ifdef CONFIG_MMU obj-$(CONFIG_ADVISE_SYSCALLS) += madvise.o
mm/compaction.c +15 -2
@@ -50,7 +50,7 @@ static inline void count_compact_events(enum vm_event_item item, long delta)#define pageblock_start_pfn (pfn) block_start_pfn(pfn, pageblock_order)#define pageblock_end_pfn (pfn) block_end_pfn(pfn, pageblock_order) -static unsigned long release_freepages (struct list_head *freelist) +unsigned long release_freepages (struct list_head *freelist) {struct page *page, *next;unsigned long high_pfn =0 ;
@@ -58,7 +58,10 @@ static unsigned long release_freepages(struct list_head *freelist)list_for_each_entry_safe (page, next, freelist, lru) {unsigned long pfn =page_to_pfn (page);list_del (&page->lru); -__free_page (page); +if (PageCompound (page)) +__free_pages (page,compound_order (page)); +else +__free_page (page);if (pfn > high_pfn) high_pfn = pfn; }
@@ -1593,6 +1596,8 @@ static enum compact_result compact_zone(struct zone *zone, struct compact_controwhile ((ret =compact_finished (zone, cc)) ==COMPACT_CONTINUE ) {int err; +int num_migrated_pages =0 ; +struct page *iter;switch (isolate_migratepages (zone, cc)) {case ISOLATE_ABORT :
@@ -1611,6 +1616,9 @@ static enum compact_result compact_zone(struct zone *zone, struct compact_contro ; } +list_for_each_entry (iter, &cc->migratepages, lru) + num_migrated_pages++; + err =migrate_pages (&cc->migratepages, compaction_alloc, compaction_free, (unsigned long )cc, cc->mode,MR_COMPACTION );
@@ -1618,6 +1626,11 @@ static enum compact_result compact_zone(struct zone *zone, struct compact_controtrace_mm_compaction_migratepages (cc->nr_migratepages, err, &cc->migratepages); +list_for_each_entry (iter, &cc->migratepages, lru) + num_migrated_pages--; + +count_vm_events (COMPACT_MIGRATE_PAGES , num_migrated_pages); +/* All pages were either migrated or will be released */ cc->nr_migratepages =0 ;if (err) {
mm/huge_memory.c +4 -0
@@ -33,6 +33,7 @@#include <linux/page_idle.h> #include <linux/shmem_fs.h> #include <linux/oom.h> +#include <linux/mem_defrag.h> #include <asm/tlb.h> #include <asm/pgalloc.h>
@@ -695,6 +696,9 @@ vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf)return VM_FAULT_OOM ;if (unlikely (khugepaged_enter (vma, vma->vm_flags)))return VM_FAULT_OOM ; +/* Make it defrag */ +if (unlikely (kmem_defragd_enter (vma, vma->vm_flags))) +return VM_FAULT_OOM ;if (!(vmf->flags &FAULT_FLAG_WRITE ) && !mm_forbids_zeropage (vma->vm_mm) &&transparent_hugepage_use_zero_page ()) {
mm/internal.h +28 -0
@@ -15,6 +15,7 @@#include <linux/mm.h> #include <linux/pagemap.h> #include <linux/tracepoint-defs.h> +#include <linux/interval_tree.h> /* * The set of flags that only affect watermark checking and reclaim
@@ -549,4 +550,31 @@ bool buffer_migrate_lock_buffers(struct buffer_head *head,int writeout (struct address_space *mapping,struct page *page);extern int exchange_two_pages (struct page *page1,struct page *page2); +struct anchor_page_info { +struct list_head list; +struct page *anchor_page; +unsigned long vaddr; +unsigned long start; +unsigned long end; +}; + +struct anchor_page_node { +struct interval_tree_node node; +unsigned long anchor_pfn;/* struct page can be calculated from pfn_to_page() */ +unsigned long anchor_vpn; +}; + +unsigned long release_freepages (struct list_head *freelist); + +void free_anchor_pages (struct vm_area_struct *vma); + +extern int exchange_two_pages (struct page *page1,struct page *page2); + +void expand (struct zone *zone,struct page *page, +int low,int high,struct free_area *area, +int migratetype); + +void prep_new_page (struct page *page,unsigned int order,gfp_t gfp_flags, +unsigned int alloc_flags); +#endif /* __MM_INTERNAL_H */
mm/khugepaged.c +1 -0
@@ -660,6 +660,7 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page, }else { src_page =pte_page (pteval);copy_user_highpage (page, src_page, address, vma); +count_vm_event (THP_COLLAPSE_MIGRATE_PAGES );VM_BUG_ON_PAGE (page_mapcount (src_page) !=1 , src_page);release_pte_page (src_page);/*
mm/madvise.c +15 -0
@@ -24,6 +24,7 @@#include <linux/swapops.h> #include <linux/shmem_fs.h> #include <linux/mmu_notifier.h> +#include <linux/mem_defrag.h> #include <asm/tlb.h>
@@ -616,6 +617,13 @@ static long madvise_remove(struct vm_area_struct *vma,return error; } +static long madvise_memdefrag (struct vm_area_struct *vma, +struct vm_area_struct **prev, +unsigned long start,unsigned long end,int behavior) +{ + *prev = vma; +return memdefrag_madvise (vma, &vma->vm_flags, behavior); +}#ifdef CONFIG_MEMORY_FAILURE /* * Error injection support for memory error handling.
@@ -697,6 +705,9 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,case MADV_FREE :case MADV_DONTNEED :return madvise_dontneed_free (vma, prev, start, end, behavior); +case MADV_MEMDEFRAG : +case MADV_NOMEMDEFRAG : +return madvise_memdefrag (vma, prev, start, end, behavior);default :return madvise_behavior (vma, prev, start, end, behavior); }
@@ -731,6 +742,8 @@ madvise_behavior_valid(int behavior)case MADV_SOFT_OFFLINE :case MADV_HWPOISON :#endif +case MADV_MEMDEFRAG : +case MADV_NOMEMDEFRAG :return true;default :
@@ -785,6 +798,8 @@ madvise_behavior_valid(int behavior)* MADV_DONTDUMP - the application wants to prevent pages in the given range * from being included in its core dump. * MADV_DODUMP - cancel MADV_DONTDUMP: no longer exclude from core dump. +* MADV_MEMDEFRAG - allow mem defrag running on this region. +* MADV_NOMEMDEFRAG - no mem defrag here. * * return values: * zero - success
mm/mem_defrag.c new file +1782 -0
1,783 lines not on this page · raw message
mm/memory.c +7 -0
@@ -69,6 +69,7 @@#include <linux/userfaultfd_k.h> #include <linux/dax.h> #include <linux/oom.h> +#include <linux/mem_defrag.h> #include <asm/io.h> #include <asm/mmu_context.h>
@@ -2926,6 +2927,9 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf)/* Allocate our own private page. */ if (unlikely (anon_vma_prepare (vma))) goto oom; +/* Make it defrag */ +if (unlikely (kmem_defragd_enter (vma, vma->vm_flags))) + goto oom; page =alloc_zeroed_user_highpage_movable (vma, vmf->address);if (!page) goto oom;
@@ -3844,6 +3848,9 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,p4d_t *p4d;vm_fault_t ret; +/* Zi: page faults modify vma */ + vma->vma_modify_jiffies = jiffies; + pgd =pgd_offset (mm, address); p4d =p4d_alloc (mm, pgd, address);if (!p4d)
mm/mmap.c +29 -0
@@ -169,6 +169,28 @@ void unlink_file_vma(struct vm_area_struct *vma) } } +void free_anchor_pages (struct vm_area_struct *vma) +{ +struct interval_tree_node *node; + +if (!vma) +return ; + +if (RB_EMPTY_ROOT (&vma->anchor_page_rb.rb_root)) +return ; + +for (node =interval_tree_iter_first (&vma->anchor_page_rb, +0 , (unsigned long )-1 ); + node;) { +struct anchor_page_node *anchor_node =container_of (node, + struct anchor_page_node, node); +interval_tree_remove (node, &vma->anchor_page_rb); + node =interval_tree_iter_next (node,0 , (unsigned long )-1 ); +kfree (anchor_node); + } + +} +/* * Close a vm structure and free it, returning the next. */
@@ -181,6 +203,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) vma->vm_ops->close (vma);if (vma->vm_file)fput (vma->vm_file); +free_anchor_pages (vma);mpol_put (vma_policy (vma));vm_area_free (vma);return next;
@@ -725,10 +748,15 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,long adjust_next =0 ;int remove_next =0 ; +/*free_anchor_pages(vma);*/ + + vma->vma_modify_jiffies = jiffies; +if (next && !insert) {struct vm_area_struct *exporter =NULL , *importer =NULL ;if (end >= next->vm_end) { +/*free_anchor_pages(next);*/ /* * vma expands, overlapping all the next, and * perhaps the one after too (mprotect case 6).
@@ -775,6 +803,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, exporter = next->vm_next; }else if (end > next->vm_start) { +/*free_anchor_pages(next);*/ /* * vma expands, overlapping part of the next: * mprotect case 5 shifting the boundary up.
mm/page_alloc.c +2 -2
@@ -1828,7 +1828,7 @@ void __init init_cma_reserved_pageblock(struct page *page)* * -- nyc */ -static inline void expand (struct zone *zone,struct page *page, +inline void expand (struct zone *zone,struct page *page,int low,int high,struct free_area *area,int migratetype) {
@@ -1950,7 +1950,7 @@ inline void post_alloc_hook(struct page *page, unsigned int order,set_page_owner (page, order, gfp_flags); } -static void prep_new_page (struct page *page,unsigned int order,gfp_t gfp_flags, +void prep_new_page (struct page *page,unsigned int order,gfp_t gfp_flags,unsigned int alloc_flags) {int i;
mm/vmstat.c +21 -0
@@ -1293,6 +1293,27 @@ const char * const vmstat_text[] = { "swap_ra", "swap_ra_hit", #endif + "memdefrag_defrag", + "memdefrag_scan", + "memdefrag_dest_free_pages", + "memdefrag_dest_anon_pages", + "memdefrag_dest_file_pages", + "memdefrag_dest_non_lru_pages", + "memdefrag_dest_free_pages_failed", + "memdefrag_dest_free_pages_overflow_failed", + "memdefrag_dest_anon_pages_failed", + "memdefrag_dest_file_pages_failed", + "memdefrag_dest_nonlru_pages_failed", + "memdefrag_src_anon_pages_failed", + "memdefrag_src_compound_pages_failed", + "memdefrag_dst_split_hugepage", +#ifdef CONFIG_COMPACTION + "compact_migrate_pages", +#endif +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + "thp_collapse_migrate_pages" +#endif + #endif /* CONFIG_VM_EVENTS_COUNTERS */ }; #endif /* CONFIG_PROC_FS || CONFIG_SYSFS || CONFIG_NUMA */
Signature · 1 line
2.20.1