[Devel,RHEL7,COMMIT] mm/memcontrol/numa_migrate: split transparent huge pages before migrating

Submitted by Konstantin Khorenko on Nov. 25, 2016, 1:53 p.m.

Details

Message ID 201611251353.uAPDrCgK021935@finist_cl7.x64_64.work.ct
State New
Series "mm/memcontrol/numa_migrate: split transparent huge pages before migrating"
Headers show

Commit Message

Konstantin Khorenko Nov. 25, 2016, 1:53 p.m.
The commit is pushed to "branch-rh7-3.10.0-327.36.1.vz7.20.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-327.36.1.vz7.20.3
------>
commit 67312c839211c56181d05434ba5fc719ef79aff2
Author: Andrey Ryabinin <aryabinin@virtuozzo.com>
Date:   Fri Nov 25 17:53:12 2016 +0400

    mm/memcontrol/numa_migrate: split transparent huge pages before migrating
    
    When migrate_pages() steps on transparent huge page it splits the page and
    migrates only a head page. Therefore after migration NR_ISOLATED_ANON counter
    is dropped by one.
    When memcg_numa_isolate_pages() isolates huge page it increase NR_ISOLATED_ANON
    counter by the number of sub-pages in hugepage, which is never dropped by
    migrate_pages() as above.
    Eventually this leads to complete lockup, because every allocation will stuck
    in too_many_isolated() loop.
    
    This patch fixes this by splitting huge page before migration and adding
    sub-pages to migration list.
    
    https://jira.sw.ru/browse/PSBM-54455
    
    Fixes: a2d6b96306e3 ("mm: memcontrol: add memory.numa_migrate file")
    Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
---
 mm/memcontrol.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index cdf1b48..6366cc0 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -5740,12 +5740,13 @@  static long memcg_numa_isolate_pages(struct lruvec *lruvec, enum lru_list lru,
 {
 	struct list_head *src = &lruvec->lists[lru];
 	struct zone *zone = lruvec_zone(lruvec);
+	struct page *page;
 	long scanned = 0, taken = 0;
 
 	spin_lock_irq(&zone->lru_lock);
 	while (!list_empty(src) && scanned < nr_to_scan && taken < nr_to_scan) {
-		struct page *page = list_last_entry(src, struct page, lru);
 		int nr_pages;
+		page = list_last_entry(src, struct page, lru);
 
 		scanned++;
 
@@ -5769,6 +5770,15 @@  static long memcg_numa_isolate_pages(struct lruvec *lruvec, enum lru_list lru,
 	__mod_zone_page_state(zone, NR_ISOLATED_ANON + is_file_lru(lru), taken);
 	spin_unlock_irq(&zone->lru_lock);
 
+	list_for_each_entry(page, dst, lru) {
+		if (PageTransHuge(page) && split_huge_page_to_list(page, dst)) {
+			list_del(&page->lru);
+			mod_zone_page_state(zone, NR_ISOLATED_ANON,
+					HPAGE_PMD_NR);
+			putback_lru_page(page);
+		}
+	}
+
 	return scanned;
 }