Skip to content

Commit b518154

Browse files
JoonsooKimtorvalds
authored andcommitted
mm/vmscan: protect the workingset on anonymous LRU
In current implementation, newly created or swap-in anonymous page is started on active list. Growing active list results in rebalancing active/inactive list so old pages on active list are demoted to inactive list. Hence, the page on active list isn't protected at all. Following is an example of this situation. Assume that 50 hot pages on active list. Numbers denote the number of pages on active/inactive list (active | inactive). 1. 50 hot pages on active list 50(h) | 0 2. workload: 50 newly created (used-once) pages 50(uo) | 50(h) 3. workload: another 50 newly created (used-once) pages 50(uo) | 50(uo), swap-out 50(h) This patch tries to fix this issue. Like as file LRU, newly created or swap-in anonymous pages will be inserted to the inactive list. They are promoted to active list if enough reference happens. This simple modification changes the above example as following. 1. 50 hot pages on active list 50(h) | 0 2. workload: 50 newly created (used-once) pages 50(h) | 50(uo) 3. workload: another 50 newly created (used-once) pages 50(h) | 50(uo), swap-out 50(uo) As you can see, hot pages on active list would be protected. Note that, this implementation has a drawback that the page cannot be promoted and will be swapped-out if re-access interval is greater than the size of inactive list but less than the size of total(active+inactive). To solve this potential issue, following patch will apply workingset detection similar to the one that's already applied to file LRU. Signed-off-by: Joonsoo Kim <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Acked-by: Johannes Weiner <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Minchan Kim <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent ccc5dc6 commit b518154

File tree

10 files changed

+19
-21
lines changed

10 files changed

+19
-21
lines changed

include/linux/swap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ extern void deactivate_page(struct page *page);
352352
extern void mark_page_lazyfree(struct page *page);
353353
extern void swap_setup(void);
354354

355-
extern void lru_cache_add_active_or_unevictable(struct page *page,
355+
extern void lru_cache_add_inactive_or_unevictable(struct page *page,
356356
struct vm_area_struct *vma);
357357

358358
/* linux/mm/vmscan.c */

kernel/events/uprobes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
184184
if (new_page) {
185185
get_page(new_page);
186186
page_add_new_anon_rmap(new_page, vma, addr, false);
187-
lru_cache_add_active_or_unevictable(new_page, vma);
187+
lru_cache_add_inactive_or_unevictable(new_page, vma);
188188
} else
189189
/* no new page, just dec_mm_counter for old_page */
190190
dec_mm_counter(mm, MM_ANONPAGES);

mm/huge_memory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf,
640640
entry = mk_huge_pmd(page, vma->vm_page_prot);
641641
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
642642
page_add_new_anon_rmap(page, vma, haddr, true);
643-
lru_cache_add_active_or_unevictable(page, vma);
643+
lru_cache_add_inactive_or_unevictable(page, vma);
644644
pgtable_trans_huge_deposit(vma->vm_mm, vmf->pmd, pgtable);
645645
set_pmd_at(vma->vm_mm, haddr, vmf->pmd, entry);
646646
add_mm_counter(vma->vm_mm, MM_ANONPAGES, HPAGE_PMD_NR);

mm/khugepaged.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,7 @@ static void collapse_huge_page(struct mm_struct *mm,
11731173
spin_lock(pmd_ptl);
11741174
BUG_ON(!pmd_none(*pmd));
11751175
page_add_new_anon_rmap(new_page, vma, address, true);
1176-
lru_cache_add_active_or_unevictable(new_page, vma);
1176+
lru_cache_add_inactive_or_unevictable(new_page, vma);
11771177
pgtable_trans_huge_deposit(mm, pmd, pgtable);
11781178
set_pmd_at(mm, address, pmd, _pmd);
11791179
update_mmu_cache_pmd(vma, address, pmd);

mm/memory.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2715,7 +2715,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
27152715
*/
27162716
ptep_clear_flush_notify(vma, vmf->address, vmf->pte);
27172717
page_add_new_anon_rmap(new_page, vma, vmf->address, false);
2718-
lru_cache_add_active_or_unevictable(new_page, vma);
2718+
lru_cache_add_inactive_or_unevictable(new_page, vma);
27192719
/*
27202720
* We call the notify macro here because, when using secondary
27212721
* mmu page tables (such as kvm shadow page tables), we want the
@@ -3266,10 +3266,9 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
32663266
/* ksm created a completely new copy */
32673267
if (unlikely(page != swapcache && swapcache)) {
32683268
page_add_new_anon_rmap(page, vma, vmf->address, false);
3269-
lru_cache_add_active_or_unevictable(page, vma);
3269+
lru_cache_add_inactive_or_unevictable(page, vma);
32703270
} else {
32713271
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
3272-
activate_page(page);
32733272
}
32743273

32753274
swap_free(entry);
@@ -3414,7 +3413,7 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
34143413

34153414
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
34163415
page_add_new_anon_rmap(page, vma, vmf->address, false);
3417-
lru_cache_add_active_or_unevictable(page, vma);
3416+
lru_cache_add_inactive_or_unevictable(page, vma);
34183417
setpte:
34193418
set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
34203419

@@ -3672,7 +3671,7 @@ vm_fault_t alloc_set_pte(struct vm_fault *vmf, struct page *page)
36723671
if (write && !(vma->vm_flags & VM_SHARED)) {
36733672
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
36743673
page_add_new_anon_rmap(page, vma, vmf->address, false);
3675-
lru_cache_add_active_or_unevictable(page, vma);
3674+
lru_cache_add_inactive_or_unevictable(page, vma);
36763675
} else {
36773676
inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
36783677
page_add_file_rmap(page, false);

mm/migrate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2830,7 +2830,7 @@ static void migrate_vma_insert_page(struct migrate_vma *migrate,
28302830
inc_mm_counter(mm, MM_ANONPAGES);
28312831
page_add_new_anon_rmap(page, vma, addr, false);
28322832
if (!is_zone_device_page(page))
2833-
lru_cache_add_active_or_unevictable(page, vma);
2833+
lru_cache_add_inactive_or_unevictable(page, vma);
28342834
get_page(page);
28352835

28362836
if (flush) {

mm/swap.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,23 +476,24 @@ void lru_cache_add(struct page *page)
476476
EXPORT_SYMBOL(lru_cache_add);
477477

478478
/**
479-
* lru_cache_add_active_or_unevictable
479+
* lru_cache_add_inactive_or_unevictable
480480
* @page: the page to be added to LRU
481481
* @vma: vma in which page is mapped for determining reclaimability
482482
*
483-
* Place @page on the active or unevictable LRU list, depending on its
483+
* Place @page on the inactive or unevictable LRU list, depending on its
484484
* evictability. Note that if the page is not evictable, it goes
485485
* directly back onto it's zone's unevictable list, it does NOT use a
486486
* per cpu pagevec.
487487
*/
488-
void lru_cache_add_active_or_unevictable(struct page *page,
488+
void lru_cache_add_inactive_or_unevictable(struct page *page,
489489
struct vm_area_struct *vma)
490490
{
491+
bool unevictable;
492+
491493
VM_BUG_ON_PAGE(PageLRU(page), page);
492494

493-
if (likely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) != VM_LOCKED))
494-
SetPageActive(page);
495-
else if (!TestSetPageMlocked(page)) {
495+
unevictable = (vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) == VM_LOCKED;
496+
if (unlikely(unevictable) && !TestSetPageMlocked(page)) {
496497
/*
497498
* We use the irq-unsafe __mod_zone_page_stat because this
498499
* counter is not modified from interrupt context, and the pte

mm/swapfile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1915,7 +1915,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
19151915
page_add_anon_rmap(page, vma, addr, false);
19161916
} else { /* ksm created a completely new copy */
19171917
page_add_new_anon_rmap(page, vma, addr, false);
1918-
lru_cache_add_active_or_unevictable(page, vma);
1918+
lru_cache_add_inactive_or_unevictable(page, vma);
19191919
}
19201920
swap_free(entry);
19211921
/*

mm/userfaultfd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
123123

124124
inc_mm_counter(dst_mm, MM_ANONPAGES);
125125
page_add_new_anon_rmap(page, dst_vma, dst_addr, false);
126-
lru_cache_add_active_or_unevictable(page, dst_vma);
126+
lru_cache_add_inactive_or_unevictable(page, dst_vma);
127127

128128
set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
129129

mm/vmscan.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -998,8 +998,6 @@ static enum page_references page_check_references(struct page *page,
998998
return PAGEREF_RECLAIM;
999999

10001000
if (referenced_ptes) {
1001-
if (PageSwapBacked(page))
1002-
return PAGEREF_ACTIVATE;
10031001
/*
10041002
* All mapped pages start out with page table
10051003
* references from the instantiating fault, so we need
@@ -1022,7 +1020,7 @@ static enum page_references page_check_references(struct page *page,
10221020
/*
10231021
* Activate file-backed executable pages after first usage.
10241022
*/
1025-
if (vm_flags & VM_EXEC)
1023+
if ((vm_flags & VM_EXEC) && !PageSwapBacked(page))
10261024
return PAGEREF_ACTIVATE;
10271025

10281026
return PAGEREF_KEEP;

0 commit comments

Comments
 (0)