aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMinchan Kim <minchan@kernel.org>2016-07-26 18:23:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-26 19:19:19 -0400
commitc6c919eb90e021fbcfcbfa9dd3d55930cdbb67f9 (patch)
treed37940fcbb29d34047aea8cef1581003880d472c
parent16d37725a042cc66f9ee95889dd40e734264508e (diff)
mm: use put_page() to free page instead of putback_lru_page()
Recently, I got many reports about perfermance degradation in embedded system(Android mobile phone, webOS TV and so on) and easy fork fail. The problem was fragmentation caused by zram and GPU driver mainly. With memory pressure, their pages were spread out all of pageblock and it cannot be migrated with current compaction algorithm which supports only LRU pages. In the end, compaction cannot work well so reclaimer shrinks all of working set pages. It made system very slow and even to fail to fork easily which requires order-[2 or 3] allocations. Other pain point is that they cannot use CMA memory space so when OOM kill happens, I can see many free pages in CMA area, which is not memory efficient. In our product which has big CMA memory, it reclaims zones too exccessively to allocate GPU and zram page although there are lots of free space in CMA so system becomes very slow easily. To solve these problem, this patch tries to add facility to migrate non-lru pages via introducing new functions and page flags to help migration. struct address_space_operations { .. .. bool (*isolate_page)(struct page *, isolate_mode_t); void (*putback_page)(struct page *); .. } new page flags PG_movable PG_isolated For details, please read description in "mm: migrate: support non-lru movable page migration". Originally, Gioh Kim had tried to support this feature but he moved so I took over the work. I took many code from his work and changed a little bit and Konstantin Khlebnikov helped Gioh a lot so he should deserve to have many credit, too. And I should mention Chulmin who have tested this patchset heavily so I can find many bugs from him. :) Thanks, Gioh, Konstantin and Chulmin! This patchset consists of five parts. 1. clean up migration mm: use put_page to free page instead of putback_lru_page 2. add non-lru page migration feature mm: migrate: support non-lru movable page migration 3. rework KVM memory-ballooning mm: balloon: use general non-lru movable page feature 4. zsmalloc refactoring for preparing page migration zsmalloc: keep max_object in size_class zsmalloc: use bit_spin_lock zsmalloc: use accessor zsmalloc: factor page chain functionality out zsmalloc: introduce zspage structure zsmalloc: separate free_zspage from putback_zspage zsmalloc: use freeobj for index 5. zsmalloc page migration zsmalloc: page migration support zram: use __GFP_MOVABLE for memory allocation This patch (of 12): Procedure of page migration is as follows: First of all, it should isolate a page from LRU and try to migrate the page. If it is successful, it releases the page for freeing. Otherwise, it should put the page back to LRU list. For LRU pages, we have used putback_lru_page for both freeing and putback to LRU list. It's okay because put_page is aware of LRU list so if it releases last refcount of the page, it removes the page from LRU list. However, It makes unnecessary operations (e.g., lru_cache_add, pagevec and flags operations. It would be not significant but no worth to do) and harder to support new non-lru page migration because put_page isn't aware of non-lru page's data structure. To solve the problem, we can add new hook in put_page with PageMovable flags check but it can increase overhead in hot path and needs new locking scheme to stabilize the flag check with put_page. So, this patch cleans it up to divide two semantic(ie, put and putback). If migration is successful, use put_page instead of putback_lru_page and use putback_lru_page only on failure. That makes code more readable and doesn't add overhead in put_page. Comment from Vlastimil "Yeah, and compaction (perhaps also other migration users) has to drain the lru pvec... Getting rid of this stuff is worth even by itself." Link: http://lkml.kernel.org/r/1464736881-24886-2-git-send-email-minchan@kernel.org Signed-off-by: Minchan Kim <minchan@kernel.org> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: Rik van Riel <riel@redhat.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Hugh Dickins <hughd@google.com> Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/migrate.c64
1 files changed, 40 insertions, 24 deletions
diff --git a/mm/migrate.c b/mm/migrate.c
index bd3fdc202e8b..c74412b381ff 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -915,6 +915,19 @@ out_unlock:
915 put_anon_vma(anon_vma); 915 put_anon_vma(anon_vma);
916 unlock_page(page); 916 unlock_page(page);
917out: 917out:
918 /*
919 * If migration is successful, decrease refcount of the newpage
920 * which will not free the page because new page owner increased
921 * refcounter. As well, if it is LRU page, add the page to LRU
922 * list in here.
923 */
924 if (rc == MIGRATEPAGE_SUCCESS) {
925 if (unlikely(__is_movable_balloon_page(newpage)))
926 put_page(newpage);
927 else
928 putback_lru_page(newpage);
929 }
930
918 return rc; 931 return rc;
919} 932}
920 933
@@ -948,6 +961,12 @@ static ICE_noinline int unmap_and_move(new_page_t get_new_page,
948 961
949 if (page_count(page) == 1) { 962 if (page_count(page) == 1) {
950 /* page was freed from under us. So we are done. */ 963 /* page was freed from under us. So we are done. */
964 ClearPageActive(page);
965 ClearPageUnevictable(page);
966 if (put_new_page)
967 put_new_page(newpage, private);
968 else
969 put_page(newpage);
951 goto out; 970 goto out;
952 } 971 }
953 972
@@ -960,10 +979,8 @@ static ICE_noinline int unmap_and_move(new_page_t get_new_page,
960 } 979 }
961 980
962 rc = __unmap_and_move(page, newpage, force, mode); 981 rc = __unmap_and_move(page, newpage, force, mode);
963 if (rc == MIGRATEPAGE_SUCCESS) { 982 if (rc == MIGRATEPAGE_SUCCESS)
964 put_new_page = NULL;
965 set_page_owner_migrate_reason(newpage, reason); 983 set_page_owner_migrate_reason(newpage, reason);
966 }
967 984
968out: 985out:
969 if (rc != -EAGAIN) { 986 if (rc != -EAGAIN) {
@@ -976,34 +993,33 @@ out:
976 list_del(&page->lru); 993 list_del(&page->lru);
977 dec_zone_page_state(page, NR_ISOLATED_ANON + 994 dec_zone_page_state(page, NR_ISOLATED_ANON +
978 page_is_file_cache(page)); 995 page_is_file_cache(page));
979 /* Soft-offlined page shouldn't go through lru cache list */ 996 }
980 if (reason == MR_MEMORY_FAILURE && rc == MIGRATEPAGE_SUCCESS) { 997
998 /*
999 * If migration is successful, releases reference grabbed during
1000 * isolation. Otherwise, restore the page to right list unless
1001 * we want to retry.
1002 */
1003 if (rc == MIGRATEPAGE_SUCCESS) {
1004 put_page(page);
1005 if (reason == MR_MEMORY_FAILURE) {
981 /* 1006 /*
982 * With this release, we free successfully migrated 1007 * Set PG_HWPoison on just freed page
983 * page and set PG_HWPoison on just freed page 1008 * intentionally. Although it's rather weird,
984 * intentionally. Although it's rather weird, it's how 1009 * it's how HWPoison flag works at the moment.
985 * HWPoison flag works at the moment.
986 */ 1010 */
987 put_page(page);
988 if (!test_set_page_hwpoison(page)) 1011 if (!test_set_page_hwpoison(page))
989 num_poisoned_pages_inc(); 1012 num_poisoned_pages_inc();
990 } else 1013 }
1014 } else {
1015 if (rc != -EAGAIN)
991 putback_lru_page(page); 1016 putback_lru_page(page);
1017 if (put_new_page)
1018 put_new_page(newpage, private);
1019 else
1020 put_page(newpage);
992 } 1021 }
993 1022
994 /*
995 * If migration was not successful and there's a freeing callback, use
996 * it. Otherwise, putback_lru_page() will drop the reference grabbed
997 * during isolation.
998 */
999 if (put_new_page)
1000 put_new_page(newpage, private);
1001 else if (unlikely(__is_movable_balloon_page(newpage))) {
1002 /* drop our reference, page already in the balloon */
1003 put_page(newpage);
1004 } else
1005 putback_lru_page(newpage);
1006
1007 if (result) { 1023 if (result) {
1008 if (rc) 1024 if (rc)
1009 *result = rc; 1025 *result = rc;