mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option
Subsystems (6)
PATCH 1/1 mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option
Changelog since v1
o Default defrag to madvise instead of never
o Introduce "defer" defrag option to wake kswapd/kcompact if THP is unavailable
o Restore "always" to historical behaviour
o Update documentation
THP defrag is enabled by default to direct reclaim/compact but not wake kswapd in the event of a THP allocation failure. The problem is that THP allocation requests potentially enter reclaim/compaction. This potentially incurs a severe stall that is not guaranteed to be offset by reduced TLB misses. While there has been considerable effort to reduce the impact of reclaim/compaction, it is still a high cost and workloads that should fit in memory fail to do so. Specifically, a simple anon/file streaming workload will enter direct reclaim on NUMA at least even though the working set size is 80% of RAM. It's been years and it's time to throw in the towel.
First, this patch defines THP defrag as follows;
madvise: A failed allocation will direct reclaim/compact if the application requests it never: Neither reclaim/compact nor wake kswapd defer: A failed allocation will wake kswapd/kcompactd always: A failed allocation will direct reclaim/compact (historical behaviour)
khugepaged defrag will enter direct/reclaim but not wake kswapd.
Next it sets the default defrag option to be "madvise" to only enter direct reclaim/compaction for applications that specifically requested it.
Lastly, it removes a check from the page allocator slowpath that is related to __GFP_THISNODE to allow "defer" to work. The callers that really cares are slub/slab and they are updated accordingly. The slab one may be surprising because it also corrects a comment as kswapd was never woken up by that path.
This means that a THP fault will no longer stall for most applications by default and the ideal for most users that get THP if they are immediately available. There are still options for users that prefer a stall at startup of a new application by either restoring historical behaviour with "always" or pick a half-way point with "defer" where kswapd does some of the work in the background and wakes kcompactd if necessary. THP defrag for khugepaged remains enabled and will enter direct/reclaim but no wakeup kswapd or kcompactd.
After this patch a THP allocation failure will quickly fallback and rely on khugepaged to recover the situation at some time in the future. In some cases, this will reduce THP usage but the benefit of THP is hard to measure and not a universal win where as a stall to reclaim/compaction is definitely measurable and can be painful.
The first test for this is using "usemem" to read a large file and write a large anonymous mapping (to avoid the zero page) multiple times. The total size of the mappings is 80% of RAM and the benchmark simply measures how long it takes to complete. It uses multiple threads to see if that is a factor. On UMA, the performance is almost identical so is not reported but on NUMA, we see this
usemem
4.4.0 4.4.0
kcompactd-v1r1 nodefrag-v1r3
Amean System-1 102.86 ( 0.00%) 46.81 ( 54.50%)
Amean System-4 37.85 ( 0.00%) 34.02 ( 10.12%)
Amean System-7 48.12 ( 0.00%) 46.89 ( 2.56%)
Amean System-12 51.98 ( 0.00%) 56.96 ( -9.57%)
Amean System-21 80.16 ( 0.00%) 79.05 ( 1.39%)
Amean System-30 110.71 ( 0.00%) 107.17 ( 3.20%)
Amean System-48 127.98 ( 0.00%) 124.83 ( 2.46%)
Amean Elapsd-1 185.84 ( 0.00%) 105.51 ( 43.23%)
Amean Elapsd-4 26.19 ( 0.00%) 25.58 ( 2.33%)
Amean Elapsd-7 21.65 ( 0.00%) 21.62 ( 0.16%)
Amean Elapsd-12 18.58 ( 0.00%) 17.94 ( 3.43%)
Amean Elapsd-21 17.53 ( 0.00%) 16.60 ( 5.33%)
Amean Elapsd-30 17.45 ( 0.00%) 17.13 ( 1.84%)
Amean Elapsd-48 15.40 ( 0.00%) 15.27 ( 0.82%)For a single thread, the benchmark completes 43.23% faster with this patch applied with smaller benefits as the thread increases. Similar, notice the large reduction in most cases in system CPU usage. The overall CPU time is
4.4.0 4.4.0
kcompactd-v1r1 nodefrag-v1r3
User 10357.65 10438.33
System 3988.88 3543.94
Elapsed 2203.01 1634.41Which is substantial. Now, the reclaim figures
4.4.0 4.4.0
kcompactd-v1r1nodefrag-v1r3
Minor Faults 128458477 278352931
Major Faults 2174976 225
Swap Ins 16904701 0
Swap Outs 17359627 0
Allocation stalls 43611 0
DMA allocs 0 0
DMA32 allocs 19832646 19448017
Normal allocs 614488453 580941839
Movable allocs 0 0
Direct pages scanned 24163800 0
Kswapd pages scanned 0 0
Kswapd pages reclaimed 0 0
Direct pages reclaimed 20691346 0
Compaction stalls 42263 0
Compaction success 938 0
Compaction failures 41325 0This patch eliminates almost all swapping and direct reclaim activity. There is still overhead but it's from NUMA balancing which does not identify that it's pointless trying to do anything with this workload.
I also tried the thpscale benchmark which forces a corner case where compaction can be used heavily and measures the latency of whether base or huge pages were used
thpscale Fault Latencies
4.4.0 4.4.0
kcompactd-v1r1 nodefrag-v1r3
Amean fault-base-1 5288.84 ( 0.00%) 2817.12 ( 46.73%)
Amean fault-base-3 6365.53 ( 0.00%) 3499.11 ( 45.03%)
Amean fault-base-5 6526.19 ( 0.00%) 4363.06 ( 33.15%)
Amean fault-base-7 7142.25 ( 0.00%) 4858.08 ( 31.98%)
Amean fault-base-12 13827.64 ( 0.00%) 10292.11 ( 25.57%)
Amean fault-base-18 18235.07 ( 0.00%) 13788.84 ( 24.38%)
Amean fault-base-24 21597.80 ( 0.00%) 24388.03 (-12.92%)
Amean fault-base-30 26754.15 ( 0.00%) 19700.55 ( 26.36%)
Amean fault-base-32 26784.94 ( 0.00%) 19513.57 ( 27.15%)
Amean fault-huge-1 4223.96 ( 0.00%) 2178.57 ( 48.42%)
Amean fault-huge-3 2194.77 ( 0.00%) 2149.74 ( 2.05%)
Amean fault-huge-5 2569.60 ( 0.00%) 2346.95 ( 8.66%)
Amean fault-huge-7 3612.69 ( 0.00%) 2997.70 ( 17.02%)
Amean fault-huge-12 3301.75 ( 0.00%) 6727.02 (-103.74%)
Amean fault-huge-18 6696.47 ( 0.00%) 6685.72 ( 0.16%)
Amean fault-huge-24 8000.72 ( 0.00%) 9311.43 (-16.38%)
Amean fault-huge-30 13305.55 ( 0.00%) 9750.45 ( 26.72%)
Amean fault-huge-32 9981.71 ( 0.00%) 10316.06 ( -3.35%)The average time to fault pages is substantially reduced in the majority of caseds but with the obvious caveat that fewer THPs are actually used in this adverse workload
4.4.0 4.4.0
kcompactd-v1r1 nodefrag-v1r3
Percentage huge-1 0.71 ( 0.00%) 14.04 (1865.22%)
Percentage huge-3 10.77 ( 0.00%) 33.05 (206.85%)
Percentage huge-5 60.39 ( 0.00%) 38.51 (-36.23%)
Percentage huge-7 45.97 ( 0.00%) 34.57 (-24.79%)
Percentage huge-12 68.12 ( 0.00%) 40.07 (-41.17%)
Percentage huge-18 64.93 ( 0.00%) 47.82 (-26.35%)
Percentage huge-24 62.69 ( 0.00%) 44.23 (-29.44%)
Percentage huge-30 43.49 ( 0.00%) 55.38 ( 27.34%)
Percentage huge-32 50.72 ( 0.00%) 51.90 ( 2.35%) 4.4.0 4.4.0
kcompactd-v1r1nodefrag-v1r3
Minor Faults 37429143 47564000
Major Faults 1916 1558
Swap Ins 1466 1079Swap Outs 2936863 149626
Allocation stalls 62510 3
DMA allocs 0 0
DMA32 allocs 6566458 6401314
Normal allocs 216361697 216538171
Movable allocs 0 0
Direct pages scanned 25977580 17998
Kswapd pages scanned 0 3638931
Kswapd pages reclaimed 0 207236
Direct pages reclaimed 8833714 88
Compaction stalls 103349 5 Compaction success 270 4 Compaction failures 103079 1
Note again that while this does swap as it's an aggressive workload, the direct relcim activity and allocation stalls is substantially reduced. There is some kswapd activity but ftrace showed that the kswapd activity was due to normal wakeups from 4K pages being allocated. Compaction-related stalls and activity are almost eliminated.
I also tried the stutter benchmark. For this, I do not have figures for NUMA but it's something that does impact UMA so I'll report what is available
stutter
4.4.0 4.4.0
kcompactd-v1r1 nodefrag-v1r3
Min mmap 7.3571 ( 0.00%) 7.3438 ( 0.18%)
1st-qrtle mmap 7.5278 ( 0.00%) 17.9200 (-138.05%)
2nd-qrtle mmap 7.6818 ( 0.00%) 21.6055 (-181.25%)
3rd-qrtle mmap 11.0889 ( 0.00%) 21.8881 (-97.39%)
Max-90% mmap 27.8978 ( 0.00%) 22.1632 ( 20.56%)
Max-93% mmap 28.3202 ( 0.00%) 22.3044 ( 21.24%)
Max-95% mmap 28.5600 ( 0.00%) 22.4580 ( 21.37%)
Max-99% mmap 29.6032 ( 0.00%) 25.5216 ( 13.79%)
Max mmap 4109.7289 ( 0.00%) 4813.9832 (-17.14%)
Mean mmap 12.4474 ( 0.00%) 19.3027 (-55.07%)This benchmark is trying to fault an anonymous mapping while there is a heavy IO load -- a scenario that desktop users used to complain about frequently. This shows a mix because the ideal case of mapping with THP is not hit as often. However, note that 99% of the mappings complete 13.79% faster. The CPU usage here is particularly interesting
4.4.0 4.4.0
kcompactd-v1r1nodefrag-v1r3User 67.50 0.99
System 1327.88 91.30
Elapsed 2079.00 2128.98
And once again we look at the reclaim figures
4.4.0 4.4.0
kcompactd-v1r1nodefrag-v1r3
Minor Faults 335241922 1314582827
Major Faults 715 819
Swap Ins 0 0
Swap Outs 0 0
Allocation stalls 532723 0
DMA allocs 0 0
DMA32 allocs 1822364341 1177950222
Normal allocs 1815640808 1517844854
Movable allocs 0 0
Direct pages scanned 21892772 0
Kswapd pages scanned 20015890 41879484
Kswapd pages reclaimed 19961986 41822072
Direct pages reclaimed 21892741 0
Compaction stalls 1065755 0
Compaction success 514 0
Compaction failures 1065241 0Allocation stalls and all direct reclaim activity is eliminated as well as compaction-related stalls.
THP gives impressive gains in some cases but only if they are quickly available. We're not going to reach the point where they are completely free so lets take the costs out of the fast paths finally and defer the cost to kswapd, kcompactd and khugepaged where it belongs.
--- Documentation/vm/transhuge.txt | 17 ++++++++ include/linux/gfp.h | 2 +- include/linux/huge_mm.h | 9 +--- mm/huge_memory.c | 99 +++++++++++++++++++++++++++--------------- mm/page_alloc.c | 8 ---- mm/slab.c | 8 ++-- mm/slub.c | 2 +- 7 files changed, 89 insertions(+), 56 deletions(-)
Documentation/vm/transhuge.txt +17 -0
@@ -113,9 +113,26 @@ guaranteed, but it may be more likely in case the allocation is for a MADV_HUGEPAGE region. echo always >/sys/kernel/mm/transparent_hugepage/defrag +echo defer >/sys/kernel/mm/transparent_hugepage/defrag echo madvise >/sys/kernel/mm/transparent_hugepage/defrag echo never >/sys/kernel/mm/transparent_hugepage/defrag +"always" means that an application requesting THP will stall on allocation +failure and directly reclaim pages and compact memory in an effort to +allocate a THP immediately. This may be desirable for virtual machines +that benefit heavily from THP use and are willing to delay the VM start +to utilise them. + +"defer" means that an application will wake kswapd in the background +to reclaim pages and wake kcompact to compact memory so that THP is +available in the near future. It's the responsibility of khugepaged +to then install the THP pages later. + +"madvise" will enter direct reclaim like "always" but only for regions +that are have used madvise(). This is the default behaviour. + +"never" should be self-explanatory. + By default kernel tries to use huge zero page on read page fault. It's possible to disable huge zero page by writing 0 or enable it back by writing 1:
include/linux/gfp.h +1 -1
@@ -248,7 +248,7 @@ struct vm_area_struct;#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | __GFP_MOVABLE)#define GFP_TRANSHUGE ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \ __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN) & \ - ~__GFP_KSWAPD_RECLAIM) + ~__GFP_RECLAIM)/* Convert GFP flags to their corresponding migrate type */ #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE)
include/linux/huge_mm.h +2 -7
@@ -39,7 +39,8 @@ int vmf_insert_pfn_pmd(struct vm_area_struct *, unsigned long addr, pmd_t *,enum transparent_hugepage_flag {TRANSPARENT_HUGEPAGE_FLAG ,TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , -TRANSPARENT_HUGEPAGE_DEFRAG_FLAG , +TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG , +TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG ,TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG ,TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG ,TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG ,
@@ -77,12 +78,6 @@ extern bool is_vma_temporary_stack(struct vm_area_struct *vma); ((__vma)->vm_flags & VM_HUGEPAGE))) && \ !((__vma)->vm_flags & VM_NOHUGEPAGE) && \ !is_vma_temporary_stack(__vma)) -#define transparent_hugepage_defrag (__vma) \ - ((transparent_hugepage_flags & \ - (1<<TRANSPARENT_HUGEPAGE_DEFRAG_FLAG)) || \ - (transparent_hugepage_flags & \ - (1<<TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG) && \ - (__vma)->vm_flags & VM_HUGEPAGE))#define transparent_hugepage_use_zero_page () \ (transparent_hugepage_flags & \ (1<<TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG))
mm/huge_memory.c +64 -35
188 lines not on this page · raw message
mm/page_alloc.c +0 -8
@@ -2998,14 +2998,6 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, (__GFP_ATOMIC|__GFP_DIRECT_RECLAIM))) gfp_mask &= ~__GFP_ATOMIC; -/* -* If this allocation cannot block and it is for a specific node, then -* fail early. There's no need to wakeup kswapd or retry for a -* speculative node-specific allocation. -*/ -if (IS_ENABLED (CONFIG_NUMA ) && (gfp_mask & __GFP_THISNODE) && !can_direct_reclaim) - goto nopage; - retry:if (gfp_mask & __GFP_KSWAPD_RECLAIM)wake_all_kswapds (order, ac);
mm/slab.c +4 -4
@@ -860,7 +860,7 @@ static inline void *____cache_alloc_node(struct kmem_cache *cachep,static inline gfp_t gfp_exact_node (gfp_t flags) { -return flags; +return flags & ~__GFP_NOFAIL; }#else /* CONFIG_NUMA */
@@ -1031,12 +1031,12 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) }/* -* Construct gfp mask to allocate from a specific node but do not direct reclaim -* or warn about failures. kswapd may still wake to reclaim in the background. +* Construct gfp mask to allocate from a specific node but do not reclaim or +* warn about failures. */ static inline gfp_t gfp_exact_node (gfp_t flags) { -return (flags | __GFP_THISNODE | __GFP_NOWARN) & ~__GFP_DIRECT_RECLAIM; +return (flags | __GFP_THISNODE | __GFP_NOWARN) & ~(__GFP_RECLAIM|__GFP_NOFAIL); }#endif
mm/slub.c +1 -1
@@ -1413,7 +1413,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)*/ alloc_gfp = (flags | __GFP_NOWARN | __GFP_NORETRY) & ~__GFP_NOFAIL;if ((alloc_gfp & __GFP_DIRECT_RECLAIM) &&oo_order (oo) >oo_order (s->min)) - alloc_gfp = (alloc_gfp | __GFP_NOMEMALLOC) & ~__GFP_DIRECT_RECLAIM; + alloc_gfp = (alloc_gfp | __GFP_NOMEMALLOC) & ~(__GFP_RECLAIM|__GFP_NOFAIL); page =alloc_slab_page (s, alloc_gfp, node, oo);if (unlikely (!page)) {
Signature · 7 lines
2.6.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 24 lines · Mel Gorman
Changelog since v1
o Default defrag to madvise instead of never
o Introduce "defer" defrag option to wake kswapd/kcompact if THP is unavailable
o Restore "always" to historical behaviour
o Update documentationTHP defrag is enabled by default to direct reclaim/compact but not wake kswapd in the event of a THP allocation failure. The problem is that THP allocation requests potentially enter reclaim/compaction. This potentially incurs a severe stall that is not guaranteed to be offset by reduced TLB misses. While there has been considerable effort to reduce the impact of reclaim/compaction, it is still a high cost and workloads that should fit in memory fail to do so. Specifically, a simple anon/file streaming workload will enter direct reclaim on NUMA at least even though the working set size is 80% of RAM. It's been years and it's time to throw in the towel.
First, this patch defines THP defrag as follows;
madvise: A failed allocation will direct reclaim/compact if the application requests it never: Neither reclaim/compact nor wake kswapd defer: A failed allocation will wake kswapd/kcompactd always: A failed allocation will direct reclaim/compact (historical behaviour)khugepaged defrag will enter direct/reclaim but not wake kswapd.
Quoted 7 lines
Next it sets the default defrag option to be "madvise" to only enter direct reclaim/compaction for applications that specifically requested it.
Lastly, it removes a check from the page allocator slowpath that is related to
__GFP_THISNODEto allow "defer" to work. The callers that really cares are slub/slab and they are updated accordingly. The slab one may be surprising because it also corrects a comment as kswapd was never woken up by that path.
It would be also nice if we could remove the is_thp_gfp_mask() checks one day. They try to make direct reclaim/compaction for THP less intrusive, but maybe could be removed now that the stalls are limited otherwise. But that's out of scope here.
[...]
Quoted 15 lines
Documentation/vm/transhuge.txt +4
-0@@ -113,9 +113,26 @@ guaranteed, but it may be more likely in case the allocation is for a MADV_HUGEPAGE region. echo always >/sys/kernel/mm/transparent_hugepage/defrag +echo defer >/sys/kernel/mm/transparent_hugepage/defrag echo madvise >/sys/kernel/mm/transparent_hugepage/defrag echo never >/sys/kernel/mm/transparent_hugepage/defrag +"always" means that an application requesting THP will stall on allocation +failure and directly reclaim pages and compact memory in an effort to +allocate a THP immediately. This may be desirable for virtual machines
Documentation/vm/transhuge.txt guaranteed, but it may be more likely in case the allocation is for a · line 132
Quoted 10 lines
Documentation/vm/transhuge.txt +10
-0+that benefit heavily from THP use and are willing to delay the VM start +to utilise them. + +"defer" means that an application will wake kswapd in the background +to reclaim pages and wake kcompact to compact memory so that THP is +available in the near future. It's the responsibility of khugepaged +to then install the THP pages later. + +"madvise" will enter direct reclaim like "always" but only for regions +that are have used madvise(). This is the default behaviour.
"madvise(MADV_HUGEPAGE)" perhaps?
[...]
Quoted 4 lines
Documentation/vm/transhuge.txt +0
-0@@ -277,17 +273,23 @@ static ssize_t double_flag_store(struct kobject *kobj, static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
Documentation/vm/transhuge.txt static ssize_t double_flag_store(struct kobject *kobj, · line 282
Quoted 10 lines
Documentation/vm/transhuge.txt +7
-3- return double_flag_show(kobj, attr, buf, - TRANSPARENT_HUGEPAGE_FLAG, - TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG); + if (test_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags)) { + VM_BUG_ON(test_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags)); + return sprintf(buf, "[always] madvise never\n"); + } else if (test_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags)) + return sprintf(buf, "always [madvise] never\n"); + else + return sprintf(buf, "always madvise [never]\n");
Somewhat ugly wrt consistent usage of { }, due to the VM_BUG_ON(), which I would just drop. Also I wonder if some racy read vs write of the file can trigger the BUG_ON? Or are the kobject accesses synchronized at a higher level?
Documentation/vm/transhuge.txt static ssize_t double_flag_store(struct kobject *kobj, · line 294
Quoted 13 lines
Documentation/vm/transhuge.txt +3
-1} + static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { ssize_t ret; - ret = double_flag_store(kobj, attr, buf, count, + ret = triple_flag_store(kobj, attr, buf, count, + TRANSPARENT_HUGEPAGE_FLAG, TRANSPARENT_HUGEPAGE_FLAG, TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG);
So this makes "echo defer > enabled" behave just like "echo always"? For userspace interface that becomes a fixed ABI, I would prefer to be more careful with unintended aliases like this. Maybe pass something like "-1" that triple_flag_store() would check in the "defer" case and return -EINVAL?
Quoted 40 lines
Documentation/vm/transhuge.txt +18
-6@@ -345,16 +347,23 @@ static ssize_t single_flag_store(struct kobject *kobj, static ssize_t defrag_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return double_flag_show(kobj, attr, buf, - TRANSPARENT_HUGEPAGE_DEFRAG_FLAG, - TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG); + if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags)) + return sprintf(buf, "[always] defer madvise never\n"); + if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags)) + return sprintf(buf, "always [defer] madvise never\n"); + else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags)) + return sprintf(buf, "always defer [madvise] never\n"); + else + return sprintf(buf, "always defer madvise [never]\n"); + } static ssize_t defrag_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - return double_flag_store(kobj, attr, buf, count, - TRANSPARENT_HUGEPAGE_DEFRAG_FLAG, + return triple_flag_store(kobj, attr, buf, count, + TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, + TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG); } static struct kobj_attribute defrag_attr =@@ -784,9 +793,30 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm, return 0; } -static inline gfp_t alloc_hugepage_gfpmask(int defrag, gfp_t extra_gfp) +/* + * If THP is set to always then directly reclaim/compact as necessary + * If set to defer then do no reclaim and defer to khugepaged + * If set to madvise and the VMA is flagged then directly reclaim/compact + */ +static inline gfp_t alloc_hugepage_direct_gfpmask(struct vm_area_struct *vma)
Documentation/vm/transhuge.txt static int __do_huge_pmd_anonymous_page(struct mm_struct *mm, · line 811
Quoted 10 lines
Documentation/vm/transhuge.txt +10
-0+{ + gfp_t reclaim_flags = 0; + + if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags) && + (vma->vm_flags & VM_HUGEPAGE)) + reclaim_flags = __GFP_DIRECT_RECLAIM; + else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags)) + reclaim_flags = __GFP_KSWAPD_RECLAIM; + else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags)) + reclaim_flags = __GFP_DIRECT_RECLAIM;
Hmm, here's a trick question. What if I wanted direct reclaim for madvise() vma's and kswapd/kcompactd for others? Right now there's no such option, right? And expressing that with different values for a single tunable becomes ugly...
(For completeness, somebody could want kswapd/kcompactd defrag only for madvise() but that's arguably not much useful)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Vlastimil Babka
Quoted 4 lines
Lastly, it removes a check from the page allocator slowpath that is related to
__GFP_THISNODEto allow "defer" to work. The callers that really cares are slub/slab and they are updated accordingly. The slab one may be surprising because it also corrects a comment as kswapd was never woken up by that path.It would be also nice if we could remove the
is_thp_gfp_mask()checks one day. They try to make direct reclaim/compaction for THP less intrusive, but maybe could be removed now that the stalls are limited otherwise. But that's out of scope here.
Maybe a rename would be appropriate but it's marginal at best.
Quoted 14 lines
[...]
Quoted 12 lines
Documentation/vm/transhuge.txt +1
-0@@ -113,9 +113,26 @@ guaranteed, but it may be more likely in case the allocation is for a MADV_HUGEPAGE region. echo always >/sys/kernel/mm/transparent_hugepage/defrag +echo defer >/sys/kernel/mm/transparent_hugepage/defrag echo madvise >/sys/kernel/mm/transparent_hugepage/defrag echo never >/sys/kernel/mm/transparent_hugepage/defrag
Quoted 16 lines
Documentation/vm/transhuge.txt guaranteed, but it may be more likely in case the allocation is for a · line 132
Quoted 13 lines
Documentation/vm/transhuge.txt +13
-0+"always" means that an application requesting THP will stall on allocation +failure and directly reclaim pages and compact memory in an effort to +allocate a THP immediately. This may be desirable for virtual machines +that benefit heavily from THP use and are willing to delay the VM start +to utilise them. + +"defer" means that an application will wake kswapd in the background +to reclaim pages and wake kcompact to compact memory so that THP is +available in the near future. It's the responsibility of khugepaged +to then install the THP pages later. + +"madvise" will enter direct reclaim like "always" but only for regions +that are have used madvise(). This is the default behaviour."
madvise(MADV_HUGEPAGE)" perhaps?
Fixed.
Quoted 11 lines
[...]
Quoted 9 lines
Documentation/vm/transhuge.txt +2
-3@@ -277,17 +273,23 @@ static ssize_t double_flag_store(struct kobject *kobj, static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return double_flag_show(kobj, attr, buf, - TRANSPARENT_HUGEPAGE_FLAG, - TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG); + if (test_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags)) { + VM_BUG_ON(test_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags));
Quoted 8 lines
Documentation/vm/transhuge.txt static ssize_t double_flag_store(struct kobject *kobj, · line 282
Quoted 5 lines
Documentation/vm/transhuge.txt +5
-0+ return sprintf(buf, "[always] madvise never\n"); + } else if (test_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags)) + return sprintf(buf, "always [madvise] never\n"); + else + return sprintf(buf, "always madvise [never]\n");Somewhat ugly wrt consistent usage of { }, due to the
VM_BUG_ON(), which I would just drop.
Fixed.
Quoted 4 lines
Also I wonder if some racy read vs write of the file can trigger the
BUG_ON? Or are the kobject accesses synchronized at a higher level?
Even if such a bug was to occur, it should be relatively harmless due to the order of the checks in alloc_hugepage_direct_gfpmask()
Quoted 8 lines
Quoted 8 lines
Documentation/vm/transhuge.txt +1
-0} + static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { ssize_t ret;
Quoted 11 lines
Documentation/vm/transhuge.txt static ssize_t double_flag_store(struct kobject *kobj, · line 294
Quoted 5 lines
Documentation/vm/transhuge.txt +2
-1- ret = double_flag_store(kobj, attr, buf, count, + ret = triple_flag_store(kobj, attr, buf, count, + TRANSPARENT_HUGEPAGE_FLAG, TRANSPARENT_HUGEPAGE_FLAG, TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG);So this makes "echo defer > enabled" behave just like "echo always"? For userspace interface that becomes a fixed ABI, I would prefer to be more careful with unintended aliases like this. Maybe pass something like "-1" that
triple_flag_store()would check in the "defer" case and return-EINVAL?
Fair point.
Quoted 18 lines
Quoted 18 lines
Documentation/vm/transhuge.txt +13
-1@@ -784,9 +793,30 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm, return 0; } -static inline gfp_t alloc_hugepage_gfpmask(int defrag, gfp_t extra_gfp) +/* + * If THP is set to always then directly reclaim/compact as necessary + * If set to defer then do no reclaim and defer to khugepaged + * If set to madvise and the VMA is flagged then directly reclaim/compact + */ +static inline gfp_t alloc_hugepage_direct_gfpmask(struct vm_area_struct *vma) +{ + gfp_t reclaim_flags = 0; + + if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags) && + (vma->vm_flags & VM_HUGEPAGE)) + reclaim_flags = __GFP_DIRECT_RECLAIM; + else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags))
Quoted 9 lines
Documentation/vm/transhuge.txt static int __do_huge_pmd_anonymous_page(struct mm_struct *mm, · line 811
Quoted 3 lines
Documentation/vm/transhuge.txt +3
-0+ reclaim_flags = __GFP_KSWAPD_RECLAIM; + else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags)) + reclaim_flags = __GFP_DIRECT_RECLAIM;Hmm, here's a trick question. What if I wanted direct reclaim for
madvise()vma's and kswapd/kcompactd for others? Right now there's no such option, right? And expressing that with different values for a single tunable becomes ugly...
It does become ugly and I'm not aware of anyone complaining about the behaviour of madvise() regions. The intent was to avoid stalls for people that are not necessarily expecting them due to the current defaults. Hence, at this point I don't think it's worth expanding the interface further.
This?
---8<---
mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option -fix
The following is a fix to the patch mm-thp-set-thp-defrag-by-default-to-madvise-and-add-a-stall-free-defrag-option.patch based on feedback from Vlastimil Babka. It removes an unnecessary VM_BUG_ON for tidyness, clarifies documentation and adds a check forbidding someone writing "defer" to the enable knob for transparent huge pages.
--- Documentation/vm/transhuge.txt | 2 +- mm/huge_memory.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-)
Documentation/vm/transhuge.txt +1 -1
@@ -129,7 +129,7 @@ available in the near future. It's the responsibility of khugepaged to then install the THP pages later. "madvise" will enter direct reclaim like "always" but only for regions -that are have used madvise(). This is the default behaviour. +that are have used madvise(MADV_HUGEPAGE). This is the default behaviour. "never" should be self-explanatory.
mm/huge_memory.c +4 -3
@@ -246,6 +246,8 @@ static ssize_t triple_flag_store(struct kobject *kobj, {if (!memcmp ("defer" , buf,min (sizeof ("defer" )-1 , count))) { +if (enabled == deferred) +return -EINVAL ;clear_bit (enabled, &transparent_hugepage_flags);clear_bit (req_madv, &transparent_hugepage_flags);set_bit (deferred, &transparent_hugepage_flags);
@@ -273,10 +275,9 @@ static ssize_t triple_flag_store(struct kobject *kobj,static ssize_t enabled_show (struct kobject *kobj,struct kobj_attribute *attr,char *buf) { -if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags)) { -VM_BUG_ON (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)); +if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags))return sprintf (buf,"[always] madvise never\n" ); - }else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)) +else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags))return sprintf (buf,"always [madvise] never\n" );else return sprintf (buf,"always madvise [never]\n" );
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Vlastimil Babka
Acked-byQuoted 5 lines · Mel Gorman
This?
---8<---
mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option -fix
Quoted 7 lines
The following is a fix to the patch mm-thp-set-thp-defrag-by-default-to-madvise-and-add-a-stall-free-defrag-option.patch based on feedback from Vlastimil Babka. It removes an unnecessary
VM_BUG_ONfor tidyness, clarifies documentation and adds a check forbidding someone writing "defer" to the enable knob for transparent huge pages.Signed-off-by Mel Gorman <[email protected]>
For the whole patch+fix.
Quoted 35 lines
--- Documentation/vm/transhuge.txt | 2 +- mm/huge_memory.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-)Documentation/vm/transhuge.txt +1
-1@@ -129,7 +129,7 @@ available in the near future. It's the responsibility of khugepaged to then install the THP pages later. "madvise" will enter direct reclaim like "always" but only for regions -that are have used madvise(). This is the default behaviour. +that are have used madvise(MADV_HUGEPAGE). This is the default behaviour. "never" should be self-explanatory.mm/huge_memory.c +2
-0@@ -246,6 +246,8 @@ static ssize_t triple_flag_store(struct kobject *kobj, {if (!memcmp ("defer" , buf,min (sizeof ("defer" )-1 , count))) { +if (enabled == deferred) +return -EINVAL ;clear_bit (enabled, &transparent_hugepage_flags);clear_bit (req_madv, &transparent_hugepage_flags);set_bit (deferred, &transparent_hugepage_flags);@@ -273,10 +275,9 @@ static ssize_t triple_flag_store(struct kobject *kobj,static ssize_t enabled_show (struct kobject *kobj,struct kobj_attribute *attr,char *buf)
mm/huge_memory.c static ssize_t triple_flag_store(struct kobject *kobj, · line 283
Quoted 10 lines
mm/huge_memory.c +2
-3{ -if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags)) { -VM_BUG_ON (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)); +if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags))return sprintf (buf,"[always] madvise never\n" ); - }else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)) +else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags))return sprintf (buf,"always [madvise] never\n" );else return sprintf (buf,"always madvise [never]\n" );
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/1 mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option -fix
The following is a fix to the patch mm-thp-set-thp-defrag-by-default-to-madvise-and-add-a-stall-free-defrag-option.patch based on feedback from Vlastimil Babka. It removes an unnecessary VM_BUG_ON for tidyness, clarifies documentation and adds a check forbidding someone writing "defer" to the enable knob for transparent huge pages. The ack from Vlastimil applies for the original patch plus this fix combined.
--- Documentation/vm/transhuge.txt | 2 +- mm/huge_memory.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-)
Documentation/vm/transhuge.txt +1 -1
@@ -129,7 +129,7 @@ available in the near future. It's the responsibility of khugepaged to then install the THP pages later. "madvise" will enter direct reclaim like "always" but only for regions -that are have used madvise(). This is the default behaviour. +that are have used madvise(MADV_HUGEPAGE). This is the default behaviour. "never" should be self-explanatory.
mm/huge_memory.c +4 -3
@@ -246,6 +246,8 @@ static ssize_t triple_flag_store(struct kobject *kobj, {if (!memcmp ("defer" , buf,min (sizeof ("defer" )-1 , count))) { +if (enabled == deferred) +return -EINVAL ;clear_bit (enabled, &transparent_hugepage_flags);clear_bit (req_madv, &transparent_hugepage_flags);set_bit (deferred, &transparent_hugepage_flags);
@@ -273,10 +275,9 @@ static ssize_t triple_flag_store(struct kobject *kobj,static ssize_t enabled_show (struct kobject *kobj,struct kobj_attribute *attr,char *buf) { -if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags)) { -VM_BUG_ON (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)); +if (test_bit (TRANSPARENT_HUGEPAGE_FLAG , &transparent_hugepage_flags))return sprintf (buf,"[always] madvise never\n" ); - }else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags)) +else if (test_bit (TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG , &transparent_hugepage_flags))return sprintf (buf,"always [madvise] never\n" );else return sprintf (buf,"always madvise [never]\n" );
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>