Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: move MADV_FREE pages into LRU_INACTIVE_FILE list

madv()'s MADV_FREE indicate pages are 'lazyfree'. They are still
anonymous pages, but they can be freed without pageout. To distinguish
these from normal anonymous pages, we clear their SwapBacked flag.

MADV_FREE pages could be freed without pageout, so they pretty much like
used once file pages. For such pages, we'd like to reclaim them once
there is memory pressure. Also it might be unfair reclaiming MADV_FREE
pages always before used once file pages and we definitively want to
reclaim the pages before other anonymous and file pages.

To speed up MADV_FREE pages reclaim, we put the pages into
LRU_INACTIVE_FILE list. The rationale is LRU_INACTIVE_FILE list is tiny
nowadays and should be full of used once file pages. Reclaiming
MADV_FREE pages will not have much interfere of anonymous and active
file pages. And the inactive file pages and MADV_FREE pages will be
reclaimed according to their age, so we don't reclaim too many MADV_FREE
pages too. Putting the MADV_FREE pages into LRU_INACTIVE_FILE_LIST also
means we can reclaim the pages without swap support. This idea is
suggested by Johannes.

This patch doesn't move MADV_FREE pages to LRU_INACTIVE_FILE list yet to
avoid bisect failure, next patch will do it.

The patch is based on Minchan's original patch.

[akpm@linux-foundation.org: coding-style fixes]
Link: http://lkml.kernel.org/r/2f87063c1e9354677b7618c647abde77b07561e5.1487965799.git.shli@fb.com
Signed-off-by: Shaohua Li <shli@fb.com>
Suggested-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Minchan Kim <minchan@kernel.org>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Hillf Danton <hillf.zj@alibaba-inc.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Shaohua Li and committed by
Linus Torvalds
f7ad2a6c d44d363f

+31 -28
+1 -1
include/linux/swap.h
··· 279 279 extern void lru_add_drain_all(void); 280 280 extern void rotate_reclaimable_page(struct page *page); 281 281 extern void deactivate_file_page(struct page *page); 282 - extern void deactivate_page(struct page *page); 282 + extern void mark_page_lazyfree(struct page *page); 283 283 extern void swap_setup(void); 284 284 285 285 extern void add_page_to_unevictable_list(struct page *page);
+1 -1
include/linux/vm_event_item.h
··· 25 25 FOR_ALL_ZONES(PGALLOC), 26 26 FOR_ALL_ZONES(ALLOCSTALL), 27 27 FOR_ALL_ZONES(PGSCAN_SKIP), 28 - PGFREE, PGACTIVATE, PGDEACTIVATE, 28 + PGFREE, PGACTIVATE, PGDEACTIVATE, PGLAZYFREE, 29 29 PGFAULT, PGMAJFAULT, 30 30 PGLAZYFREED, 31 31 PGREFILL,
-3
mm/huge_memory.c
··· 1564 1564 ClearPageDirty(page); 1565 1565 unlock_page(page); 1566 1566 1567 - if (PageActive(page)) 1568 - deactivate_page(page); 1569 - 1570 1567 if (pmd_young(orig_pmd) || pmd_dirty(orig_pmd)) { 1571 1568 pmdp_invalidate(vma, addr, pmd); 1572 1569 orig_pmd = pmd_mkold(orig_pmd);
-2
mm/madvise.c
··· 411 411 ptent = pte_mkold(ptent); 412 412 ptent = pte_mkclean(ptent); 413 413 set_pte_at(mm, addr, pte, ptent); 414 - if (PageActive(page)) 415 - deactivate_page(page); 416 414 tlb_remove_tlb_entry(tlb, pte, addr); 417 415 } 418 416 }
+28 -21
mm/swap.c
··· 46 46 static DEFINE_PER_CPU(struct pagevec, lru_add_pvec); 47 47 static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); 48 48 static DEFINE_PER_CPU(struct pagevec, lru_deactivate_file_pvecs); 49 - static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); 49 + static DEFINE_PER_CPU(struct pagevec, lru_lazyfree_pvecs); 50 50 #ifdef CONFIG_SMP 51 51 static DEFINE_PER_CPU(struct pagevec, activate_page_pvecs); 52 52 #endif ··· 571 571 } 572 572 573 573 574 - static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec, 574 + static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec, 575 575 void *arg) 576 576 { 577 - if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { 578 - int file = page_is_file_cache(page); 579 - int lru = page_lru_base_type(page); 577 + if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) && 578 + !PageUnevictable(page)) { 579 + bool active = PageActive(page); 580 580 581 - del_page_from_lru_list(page, lruvec, lru + LRU_ACTIVE); 581 + del_page_from_lru_list(page, lruvec, 582 + LRU_INACTIVE_ANON + active); 582 583 ClearPageActive(page); 583 584 ClearPageReferenced(page); 584 - add_page_to_lru_list(page, lruvec, lru); 585 + /* 586 + * lazyfree pages are clean anonymous pages. They have 587 + * SwapBacked flag cleared to distinguish normal anonymous 588 + * pages 589 + */ 590 + ClearPageSwapBacked(page); 591 + add_page_to_lru_list(page, lruvec, LRU_INACTIVE_FILE); 585 592 586 - __count_vm_event(PGDEACTIVATE); 587 - update_page_reclaim_stat(lruvec, file, 0); 593 + __count_vm_events(PGLAZYFREE, hpage_nr_pages(page)); 594 + update_page_reclaim_stat(lruvec, 1, 0); 588 595 } 589 596 } 590 597 ··· 621 614 if (pagevec_count(pvec)) 622 615 pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL); 623 616 624 - pvec = &per_cpu(lru_deactivate_pvecs, cpu); 617 + pvec = &per_cpu(lru_lazyfree_pvecs, cpu); 625 618 if (pagevec_count(pvec)) 626 - pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); 619 + pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL); 627 620 628 621 activate_page_drain(cpu); 629 622 } ··· 655 648 } 656 649 657 650 /** 658 - * deactivate_page - deactivate a page 651 + * mark_page_lazyfree - make an anon page lazyfree 659 652 * @page: page to deactivate 660 653 * 661 - * deactivate_page() moves @page to the inactive list if @page was on the active 662 - * list and was not an unevictable page. This is done to accelerate the reclaim 663 - * of @page. 654 + * mark_page_lazyfree() moves @page to the inactive file list. 655 + * This is done to accelerate the reclaim of @page. 664 656 */ 665 - void deactivate_page(struct page *page) 657 + void mark_page_lazyfree(struct page *page) 666 658 { 667 - if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { 668 - struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs); 659 + if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) && 660 + !PageUnevictable(page)) { 661 + struct pagevec *pvec = &get_cpu_var(lru_lazyfree_pvecs); 669 662 670 663 get_page(page); 671 664 if (!pagevec_add(pvec, page) || PageCompound(page)) 672 - pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); 673 - put_cpu_var(lru_deactivate_pvecs); 665 + pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL); 666 + put_cpu_var(lru_lazyfree_pvecs); 674 667 } 675 668 } 676 669 ··· 710 703 if (pagevec_count(&per_cpu(lru_add_pvec, cpu)) || 711 704 pagevec_count(&per_cpu(lru_rotate_pvecs, cpu)) || 712 705 pagevec_count(&per_cpu(lru_deactivate_file_pvecs, cpu)) || 713 - pagevec_count(&per_cpu(lru_deactivate_pvecs, cpu)) || 706 + pagevec_count(&per_cpu(lru_lazyfree_pvecs, cpu)) || 714 707 need_activate_page_drain(cpu)) { 715 708 INIT_WORK(work, lru_add_drain_per_cpu); 716 709 queue_work_on(cpu, mm_percpu_wq, work);
+1
mm/vmstat.c
··· 991 991 "pgfree", 992 992 "pgactivate", 993 993 "pgdeactivate", 994 + "pglazyfree", 994 995 995 996 "pgfault", 996 997 "pgmajfault",