diff options
| author | Minchan Kim <minchan@kernel.org> | 2013-05-09 16:21:27 +0900 |
|---|---|---|
| committer | Kyle Yan <kyan@codeaurora.org> | 2016-06-22 14:43:57 -0700 |
| commit | 06de050ac6a250930f5b95dc153744d64dbc13c4 (patch) | |
| tree | 7d78780c17783725e3699e0a414160ce52dcd500 | |
| parent | a4e92011d44d60ad33dca31785682ddb82c44e40 (diff) | |
mm: Enhance per process reclaim to consider shared pages
Some pages could be shared by several processes. (ex, libc)
In case of that, it's too bad to reclaim them from the beginnig.
This patch causes VM to keep them on memory until last task
try to reclaim them so shared pages will be reclaimed only if
all of task has gone swapping out.
This feature doesn't handle non-linear mapping on ramfs because
it's very time-consuming and doesn't make sure of reclaiming and
not common.
Change-Id: I7e5f34f2e947f5db6d405867fe2ad34863ca40f7
Signed-off-by: Sangseok Lee <sangseok.lee@lge.com>
Signed-off-by: Minchan Kim <minchan@kernel.org>
Patch-mainline: linux-mm @ 9 May 2013 16:21:27
[vinmenon@codeaurora.org: trivial merge conflict fixes + changes
to make the patch work with 3.18 kernel]
Signed-off-by: Vinayak Menon <vinmenon@codeaurora.org>
| -rw-r--r-- | fs/proc/task_mmu.c | 2 | ||||
| -rw-r--r-- | include/linux/rmap.h | 9 | ||||
| -rw-r--r-- | mm/internal.h | 2 | ||||
| -rw-r--r-- | mm/ksm.c | 6 | ||||
| -rw-r--r-- | mm/memory-failure.c | 2 | ||||
| -rw-r--r-- | mm/migrate.c | 4 | ||||
| -rw-r--r-- | mm/rmap.c | 20 | ||||
| -rw-r--r-- | mm/vmscan.c | 14 |
8 files changed, 47 insertions, 12 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index e003d3d691f7..6af5d64a79a8 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -1467,7 +1467,7 @@ cont: break; } pte_unmap_unlock(pte - 1, ptl); - reclaim_pages_from_list(&page_list); + reclaim_pages_from_list(&page_list, vma); if (addr != end) goto cont; diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 7536f000c77c..e72b85737a99 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -12,7 +12,8 @@ extern int isolate_lru_page(struct page *page); extern void putback_lru_page(struct page *page); -extern unsigned long reclaim_pages_from_list(struct list_head *page_list); +extern unsigned long reclaim_pages_from_list(struct list_head *page_list, + struct vm_area_struct *vma); /* * The anon_vma heads a list of private "related" vmas, to scan if @@ -180,7 +181,8 @@ int page_referenced(struct page *, int is_locked, #define TTU_ACTION(x) ((x) & TTU_ACTION_MASK) -int try_to_unmap(struct page *, enum ttu_flags flags); +int try_to_unmap(struct page *, enum ttu_flags flags, + struct vm_area_struct *vma); /* * Used by uprobes to replace a userspace page safely @@ -236,6 +238,7 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma); */ struct rmap_walk_control { void *arg; + struct vm_area_struct *target_vma; int (*rmap_one)(struct page *page, struct vm_area_struct *vma, unsigned long addr, void *arg); int (*done)(struct page *page); @@ -259,7 +262,7 @@ static inline int page_referenced(struct page *page, int is_locked, return 0; } -#define try_to_unmap(page, refs) SWAP_FAIL +#define try_to_unmap(page, refs, vma) SWAP_FAIL static inline int page_mkclean(struct page *page) { diff --git a/mm/internal.h b/mm/internal.h index 38e24b89e4c4..899f7b332a0b 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -309,10 +309,8 @@ static inline void mlock_migrate_page(struct page *newpage, struct page *page) extern pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma); -#ifdef CONFIG_TRANSPARENT_HUGEPAGE extern unsigned long vma_address(struct page *page, struct vm_area_struct *vma); -#endif #else /* !CONFIG_MMU */ static inline void clear_page_mlock(struct page *page) { } static inline void mlock_vma_page(struct page *page) { } @@ -1984,6 +1984,12 @@ int rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc) stable_node = page_stable_node(page); if (!stable_node) return ret; + + if (rwc->target_vma) { + unsigned long address = vma_address(page, rwc->target_vma); + ret = rwc->rmap_one(page, rwc->target_vma, address, rwc->arg); + goto out; + } again: hlist_for_each_entry(rmap_item, &stable_node->hlist, hlist) { struct anon_vma *anon_vma = rmap_item->anon_vma; diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 750b7893ee3a..b3becd9941e9 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1009,7 +1009,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, if (kill) collect_procs(hpage, &tokill, flags & MF_ACTION_REQUIRED); - ret = try_to_unmap(hpage, ttu); + ret = try_to_unmap(hpage, ttu, NULL); if (ret != SWAP_SUCCESS) printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n", pfn, page_mapcount(hpage)); diff --git a/mm/migrate.c b/mm/migrate.c index 8fcf935c863e..85b91eb19ea9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -888,7 +888,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage, VM_BUG_ON_PAGE(PageAnon(page) && !PageKsm(page) && !anon_vma, page); try_to_unmap(page, - TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS); + TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS, NULL); page_was_mapped = 1; } @@ -1052,7 +1052,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, if (page_mapped(hpage)) { try_to_unmap(hpage, - TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS); + TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS, NULL); page_was_mapped = 1; } diff --git a/mm/rmap.c b/mm/rmap.c index b577fbb98d4b..59489b3b1ac6 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1459,9 +1459,12 @@ static int page_not_mapped(struct page *page) * try_to_unmap - try to remove all page table mappings to a page * @page: the page to get unmapped * @flags: action and flags + * @vma : target vma for reclaim * * Tries to remove all the page table entries which are mapping this * page, used in the pageout path. Caller must hold the page lock. + * If @vma is not NULL, this function try to remove @page from only @vma + * without peeking all mapped vma for @page. * Return values are: * * SWAP_SUCCESS - we succeeded in removing all mappings @@ -1469,7 +1472,8 @@ static int page_not_mapped(struct page *page) * SWAP_FAIL - the page is unswappable * SWAP_MLOCK - page is mlocked. */ -int try_to_unmap(struct page *page, enum ttu_flags flags) +int try_to_unmap(struct page *page, enum ttu_flags flags, + struct vm_area_struct *vma) { int ret; struct rmap_walk_control rwc = { @@ -1477,6 +1481,7 @@ int try_to_unmap(struct page *page, enum ttu_flags flags) .arg = (void *)flags, .done = page_not_mapped, .anon_lock = page_lock_anon_vma_read, + .target_vma = vma, }; VM_BUG_ON_PAGE(!PageHuge(page) && PageTransHuge(page), page); @@ -1522,6 +1527,7 @@ int try_to_munlock(struct page *page) .arg = (void *)TTU_MUNLOCK, .done = page_not_mapped, .anon_lock = page_lock_anon_vma_read, + .target_vma = NULL, }; @@ -1583,6 +1589,11 @@ static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc) struct anon_vma_chain *avc; int ret = SWAP_AGAIN; + if (rwc->target_vma) { + unsigned long address = vma_address(page, rwc->target_vma); + return rwc->rmap_one(page, rwc->target_vma, address, rwc->arg); + } + anon_vma = rmap_walk_anon_lock(page, rwc); if (!anon_vma) return ret; @@ -1625,6 +1636,7 @@ static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc) struct address_space *mapping = page->mapping; pgoff_t pgoff; struct vm_area_struct *vma; + unsigned long address; int ret = SWAP_AGAIN; /* @@ -1640,6 +1652,12 @@ static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc) pgoff = page_to_pgoff(page); i_mmap_lock_read(mapping); + if (rwc->target_vma) { + address = vma_address(page, rwc->target_vma); + ret = rwc->rmap_one(page, rwc->target_vma, address, rwc->arg); + goto done; + } + vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) { unsigned long address = vma_address(page, vma); diff --git a/mm/vmscan.c b/mm/vmscan.c index fe9c39e6b900..73f5cec91063 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -105,6 +105,13 @@ struct scan_control { /* Number of pages freed so far during a call to shrink_zones() */ unsigned long nr_reclaimed; + + /* + * Reclaim pages from a vma. If the page is shared by other tasks + * it is zapped from a vma without reclaim so it ends up remaining + * on memory until last task zap it. + */ + struct vm_area_struct *target_vma; }; #define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru)) @@ -1115,7 +1122,8 @@ static unsigned long shrink_page_list(struct list_head *page_list, */ if (page_mapped(page) && mapping) { switch (try_to_unmap(page, - ttu_flags|TTU_BATCH_FLUSH)) { + ttu_flags|TTU_BATCH_FLUSH, + sc->target_vma)) { case SWAP_FAIL: goto activate_locked; case SWAP_AGAIN: @@ -1324,7 +1332,8 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, } #ifdef CONFIG_PROCESS_RECLAIM -unsigned long reclaim_pages_from_list(struct list_head *page_list) +unsigned long reclaim_pages_from_list(struct list_head *page_list, + struct vm_area_struct *vma) { struct scan_control sc = { .gfp_mask = GFP_KERNEL, @@ -1332,6 +1341,7 @@ unsigned long reclaim_pages_from_list(struct list_head *page_list) .may_writepage = 1, .may_unmap = 1, .may_swap = 1, + .target_vma = vma, }; unsigned long nr_reclaimed; |
