diff options
author | Mel Gorman <mel@csn.ul.ie> | 2011-01-13 18:45:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 20:32:34 -0500 |
commit | 77f1fe6b08b13a87391549c8a820ddc817b6f50e (patch) | |
tree | 720865bd0994da3787b6f37d33b2ee4c26a2de6c /mm | |
parent | 3e7d344970673c5334cf7b5bb27c8c0942b06126 (diff) |
mm: migration: allow migration to operate asynchronously and avoid synchronous compaction in the faster path
Migration synchronously waits for writeback if the initial passes fails.
Callers of memory compaction do not necessarily want this behaviour if the
caller is latency sensitive or expects that synchronous migration is not
going to have a significantly better success rate.
This patch adds a sync parameter to migrate_pages() allowing the caller to
indicate if wait_on_page_writeback() is allowed within migration or not.
For reclaim/compaction, try_to_compact_pages() is first called
asynchronously, direct reclaim runs and then try_to_compact_pages() is
called synchronously as there is a greater expectation that it'll succeed.
[akpm@linux-foundation.org: build/merge fix]
Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Rik van Riel <riel@redhat.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Andy Whitcroft <apw@shadowen.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/compaction.c | 14 | ||||
-rw-r--r-- | mm/memory-failure.c | 8 | ||||
-rw-r--r-- | mm/memory_hotplug.c | 3 | ||||
-rw-r--r-- | mm/mempolicy.c | 4 | ||||
-rw-r--r-- | mm/migrate.c | 22 | ||||
-rw-r--r-- | mm/page_alloc.c | 21 | ||||
-rw-r--r-- | mm/vmscan.c | 3 |
7 files changed, 49 insertions, 26 deletions
diff --git a/mm/compaction.c b/mm/compaction.c index 8fe917ec7c11..47fca1069343 100644 --- a/mm/compaction.c +++ b/mm/compaction.c | |||
@@ -33,6 +33,7 @@ struct compact_control { | |||
33 | unsigned long nr_migratepages; /* Number of pages to migrate */ | 33 | unsigned long nr_migratepages; /* Number of pages to migrate */ |
34 | unsigned long free_pfn; /* isolate_freepages search base */ | 34 | unsigned long free_pfn; /* isolate_freepages search base */ |
35 | unsigned long migrate_pfn; /* isolate_migratepages search base */ | 35 | unsigned long migrate_pfn; /* isolate_migratepages search base */ |
36 | bool sync; /* Synchronous migration */ | ||
36 | 37 | ||
37 | /* Account for isolated anon and file pages */ | 38 | /* Account for isolated anon and file pages */ |
38 | unsigned long nr_anon; | 39 | unsigned long nr_anon; |
@@ -455,7 +456,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) | |||
455 | 456 | ||
456 | nr_migrate = cc->nr_migratepages; | 457 | nr_migrate = cc->nr_migratepages; |
457 | migrate_pages(&cc->migratepages, compaction_alloc, | 458 | migrate_pages(&cc->migratepages, compaction_alloc, |
458 | (unsigned long)cc, 0); | 459 | (unsigned long)cc, 0, |
460 | cc->sync); | ||
459 | update_nr_listpages(cc); | 461 | update_nr_listpages(cc); |
460 | nr_remaining = cc->nr_migratepages; | 462 | nr_remaining = cc->nr_migratepages; |
461 | 463 | ||
@@ -482,7 +484,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) | |||
482 | } | 484 | } |
483 | 485 | ||
484 | unsigned long compact_zone_order(struct zone *zone, | 486 | unsigned long compact_zone_order(struct zone *zone, |
485 | int order, gfp_t gfp_mask) | 487 | int order, gfp_t gfp_mask, |
488 | bool sync) | ||
486 | { | 489 | { |
487 | struct compact_control cc = { | 490 | struct compact_control cc = { |
488 | .nr_freepages = 0, | 491 | .nr_freepages = 0, |
@@ -490,6 +493,7 @@ unsigned long compact_zone_order(struct zone *zone, | |||
490 | .order = order, | 493 | .order = order, |
491 | .migratetype = allocflags_to_migratetype(gfp_mask), | 494 | .migratetype = allocflags_to_migratetype(gfp_mask), |
492 | .zone = zone, | 495 | .zone = zone, |
496 | .sync = sync, | ||
493 | }; | 497 | }; |
494 | INIT_LIST_HEAD(&cc.freepages); | 498 | INIT_LIST_HEAD(&cc.freepages); |
495 | INIT_LIST_HEAD(&cc.migratepages); | 499 | INIT_LIST_HEAD(&cc.migratepages); |
@@ -505,11 +509,13 @@ int sysctl_extfrag_threshold = 500; | |||
505 | * @order: The order of the current allocation | 509 | * @order: The order of the current allocation |
506 | * @gfp_mask: The GFP mask of the current allocation | 510 | * @gfp_mask: The GFP mask of the current allocation |
507 | * @nodemask: The allowed nodes to allocate from | 511 | * @nodemask: The allowed nodes to allocate from |
512 | * @sync: Whether migration is synchronous or not | ||
508 | * | 513 | * |
509 | * This is the main entry point for direct page compaction. | 514 | * This is the main entry point for direct page compaction. |
510 | */ | 515 | */ |
511 | unsigned long try_to_compact_pages(struct zonelist *zonelist, | 516 | unsigned long try_to_compact_pages(struct zonelist *zonelist, |
512 | int order, gfp_t gfp_mask, nodemask_t *nodemask) | 517 | int order, gfp_t gfp_mask, nodemask_t *nodemask, |
518 | bool sync) | ||
513 | { | 519 | { |
514 | enum zone_type high_zoneidx = gfp_zone(gfp_mask); | 520 | enum zone_type high_zoneidx = gfp_zone(gfp_mask); |
515 | int may_enter_fs = gfp_mask & __GFP_FS; | 521 | int may_enter_fs = gfp_mask & __GFP_FS; |
@@ -533,7 +539,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, | |||
533 | nodemask) { | 539 | nodemask) { |
534 | int status; | 540 | int status; |
535 | 541 | ||
536 | status = compact_zone_order(zone, order, gfp_mask); | 542 | status = compact_zone_order(zone, order, gfp_mask, sync); |
537 | rc = max(status, rc); | 543 | rc = max(status, rc); |
538 | 544 | ||
539 | /* If a normal allocation would succeed, stop compacting */ | 545 | /* If a normal allocation would succeed, stop compacting */ |
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 46ab2c044b0e..2323a8039a98 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c | |||
@@ -1290,9 +1290,10 @@ static int soft_offline_huge_page(struct page *page, int flags) | |||
1290 | /* Keep page count to indicate a given hugepage is isolated. */ | 1290 | /* Keep page count to indicate a given hugepage is isolated. */ |
1291 | 1291 | ||
1292 | list_add(&hpage->lru, &pagelist); | 1292 | list_add(&hpage->lru, &pagelist); |
1293 | ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0); | 1293 | ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0, |
1294 | true); | ||
1294 | if (ret) { | 1295 | if (ret) { |
1295 | putback_lru_pages(&pagelist); | 1296 | putback_lru_pages(&pagelist); |
1296 | pr_debug("soft offline: %#lx: migration failed %d, type %lx\n", | 1297 | pr_debug("soft offline: %#lx: migration failed %d, type %lx\n", |
1297 | pfn, ret, page->flags); | 1298 | pfn, ret, page->flags); |
1298 | if (ret > 0) | 1299 | if (ret > 0) |
@@ -1413,7 +1414,8 @@ int soft_offline_page(struct page *page, int flags) | |||
1413 | LIST_HEAD(pagelist); | 1414 | LIST_HEAD(pagelist); |
1414 | 1415 | ||
1415 | list_add(&page->lru, &pagelist); | 1416 | list_add(&page->lru, &pagelist); |
1416 | ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0); | 1417 | ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, |
1418 | 0, true); | ||
1417 | if (ret) { | 1419 | if (ret) { |
1418 | pr_info("soft offline: %#lx: migration failed %d, type %lx\n", | 1420 | pr_info("soft offline: %#lx: migration failed %d, type %lx\n", |
1419 | pfn, ret, page->flags); | 1421 | pfn, ret, page->flags); |
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 2c6523af5473..584fc5588fdd 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -733,7 +733,8 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn) | |||
733 | goto out; | 733 | goto out; |
734 | } | 734 | } |
735 | /* this function returns # of failed pages */ | 735 | /* this function returns # of failed pages */ |
736 | ret = migrate_pages(&source, hotremove_migrate_alloc, 0, 1); | 736 | ret = migrate_pages(&source, hotremove_migrate_alloc, 0, |
737 | 1, true); | ||
737 | if (ret) | 738 | if (ret) |
738 | putback_lru_pages(&source); | 739 | putback_lru_pages(&source); |
739 | } | 740 | } |
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 11ff260fb282..9db274593086 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -935,7 +935,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest, | |||
935 | return PTR_ERR(vma); | 935 | return PTR_ERR(vma); |
936 | 936 | ||
937 | if (!list_empty(&pagelist)) { | 937 | if (!list_empty(&pagelist)) { |
938 | err = migrate_pages(&pagelist, new_node_page, dest, 0); | 938 | err = migrate_pages(&pagelist, new_node_page, dest, 0, true); |
939 | if (err) | 939 | if (err) |
940 | putback_lru_pages(&pagelist); | 940 | putback_lru_pages(&pagelist); |
941 | } | 941 | } |
@@ -1155,7 +1155,7 @@ static long do_mbind(unsigned long start, unsigned long len, | |||
1155 | 1155 | ||
1156 | if (!list_empty(&pagelist)) { | 1156 | if (!list_empty(&pagelist)) { |
1157 | nr_failed = migrate_pages(&pagelist, new_vma_page, | 1157 | nr_failed = migrate_pages(&pagelist, new_vma_page, |
1158 | (unsigned long)vma, 0); | 1158 | (unsigned long)vma, 0, true); |
1159 | if (nr_failed) | 1159 | if (nr_failed) |
1160 | putback_lru_pages(&pagelist); | 1160 | putback_lru_pages(&pagelist); |
1161 | } | 1161 | } |
diff --git a/mm/migrate.c b/mm/migrate.c index 94875b265928..dc47f6c40353 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
@@ -614,7 +614,7 @@ static int move_to_new_page(struct page *newpage, struct page *page, | |||
614 | * to the newly allocated page in newpage. | 614 | * to the newly allocated page in newpage. |
615 | */ | 615 | */ |
616 | static int unmap_and_move(new_page_t get_new_page, unsigned long private, | 616 | static int unmap_and_move(new_page_t get_new_page, unsigned long private, |
617 | struct page *page, int force, int offlining) | 617 | struct page *page, int force, int offlining, bool sync) |
618 | { | 618 | { |
619 | int rc = 0; | 619 | int rc = 0; |
620 | int *result = NULL; | 620 | int *result = NULL; |
@@ -682,7 +682,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
682 | BUG_ON(charge); | 682 | BUG_ON(charge); |
683 | 683 | ||
684 | if (PageWriteback(page)) { | 684 | if (PageWriteback(page)) { |
685 | if (!force) | 685 | if (!force || !sync) |
686 | goto uncharge; | 686 | goto uncharge; |
687 | wait_on_page_writeback(page); | 687 | wait_on_page_writeback(page); |
688 | } | 688 | } |
@@ -827,7 +827,7 @@ move_newpage: | |||
827 | */ | 827 | */ |
828 | static int unmap_and_move_huge_page(new_page_t get_new_page, | 828 | static int unmap_and_move_huge_page(new_page_t get_new_page, |
829 | unsigned long private, struct page *hpage, | 829 | unsigned long private, struct page *hpage, |
830 | int force, int offlining) | 830 | int force, int offlining, bool sync) |
831 | { | 831 | { |
832 | int rc = 0; | 832 | int rc = 0; |
833 | int *result = NULL; | 833 | int *result = NULL; |
@@ -841,7 +841,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, | |||
841 | rc = -EAGAIN; | 841 | rc = -EAGAIN; |
842 | 842 | ||
843 | if (!trylock_page(hpage)) { | 843 | if (!trylock_page(hpage)) { |
844 | if (!force) | 844 | if (!force || !sync) |
845 | goto out; | 845 | goto out; |
846 | lock_page(hpage); | 846 | lock_page(hpage); |
847 | } | 847 | } |
@@ -909,7 +909,8 @@ out: | |||
909 | * Return: Number of pages not migrated or error code. | 909 | * Return: Number of pages not migrated or error code. |
910 | */ | 910 | */ |
911 | int migrate_pages(struct list_head *from, | 911 | int migrate_pages(struct list_head *from, |
912 | new_page_t get_new_page, unsigned long private, int offlining) | 912 | new_page_t get_new_page, unsigned long private, int offlining, |
913 | bool sync) | ||
913 | { | 914 | { |
914 | int retry = 1; | 915 | int retry = 1; |
915 | int nr_failed = 0; | 916 | int nr_failed = 0; |
@@ -929,7 +930,8 @@ int migrate_pages(struct list_head *from, | |||
929 | cond_resched(); | 930 | cond_resched(); |
930 | 931 | ||
931 | rc = unmap_and_move(get_new_page, private, | 932 | rc = unmap_and_move(get_new_page, private, |
932 | page, pass > 2, offlining); | 933 | page, pass > 2, offlining, |
934 | sync); | ||
933 | 935 | ||
934 | switch(rc) { | 936 | switch(rc) { |
935 | case -ENOMEM: | 937 | case -ENOMEM: |
@@ -958,7 +960,8 @@ out: | |||
958 | } | 960 | } |
959 | 961 | ||
960 | int migrate_huge_pages(struct list_head *from, | 962 | int migrate_huge_pages(struct list_head *from, |
961 | new_page_t get_new_page, unsigned long private, int offlining) | 963 | new_page_t get_new_page, unsigned long private, int offlining, |
964 | bool sync) | ||
962 | { | 965 | { |
963 | int retry = 1; | 966 | int retry = 1; |
964 | int nr_failed = 0; | 967 | int nr_failed = 0; |
@@ -974,7 +977,8 @@ int migrate_huge_pages(struct list_head *from, | |||
974 | cond_resched(); | 977 | cond_resched(); |
975 | 978 | ||
976 | rc = unmap_and_move_huge_page(get_new_page, | 979 | rc = unmap_and_move_huge_page(get_new_page, |
977 | private, page, pass > 2, offlining); | 980 | private, page, pass > 2, offlining, |
981 | sync); | ||
978 | 982 | ||
979 | switch(rc) { | 983 | switch(rc) { |
980 | case -ENOMEM: | 984 | case -ENOMEM: |
@@ -1107,7 +1111,7 @@ set_status: | |||
1107 | err = 0; | 1111 | err = 0; |
1108 | if (!list_empty(&pagelist)) { | 1112 | if (!list_empty(&pagelist)) { |
1109 | err = migrate_pages(&pagelist, new_page_node, | 1113 | err = migrate_pages(&pagelist, new_page_node, |
1110 | (unsigned long)pm, 0); | 1114 | (unsigned long)pm, 0, true); |
1111 | if (err) | 1115 | if (err) |
1112 | putback_lru_pages(&pagelist); | 1116 | putback_lru_pages(&pagelist); |
1113 | } | 1117 | } |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 03a66a31bfcd..0fd486467b4b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -1812,7 +1812,8 @@ static struct page * | |||
1812 | __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | 1812 | __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, |
1813 | struct zonelist *zonelist, enum zone_type high_zoneidx, | 1813 | struct zonelist *zonelist, enum zone_type high_zoneidx, |
1814 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, | 1814 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, |
1815 | int migratetype, unsigned long *did_some_progress) | 1815 | int migratetype, unsigned long *did_some_progress, |
1816 | bool sync_migration) | ||
1816 | { | 1817 | { |
1817 | struct page *page; | 1818 | struct page *page; |
1818 | struct task_struct *tsk = current; | 1819 | struct task_struct *tsk = current; |
@@ -1822,7 +1823,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | |||
1822 | 1823 | ||
1823 | tsk->flags |= PF_MEMALLOC; | 1824 | tsk->flags |= PF_MEMALLOC; |
1824 | *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, | 1825 | *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, |
1825 | nodemask); | 1826 | nodemask, sync_migration); |
1826 | tsk->flags &= ~PF_MEMALLOC; | 1827 | tsk->flags &= ~PF_MEMALLOC; |
1827 | if (*did_some_progress != COMPACT_SKIPPED) { | 1828 | if (*did_some_progress != COMPACT_SKIPPED) { |
1828 | 1829 | ||
@@ -1859,7 +1860,8 @@ static inline struct page * | |||
1859 | __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, | 1860 | __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, |
1860 | struct zonelist *zonelist, enum zone_type high_zoneidx, | 1861 | struct zonelist *zonelist, enum zone_type high_zoneidx, |
1861 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, | 1862 | nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, |
1862 | int migratetype, unsigned long *did_some_progress) | 1863 | int migratetype, unsigned long *did_some_progress, |
1864 | bool sync_migration) | ||
1863 | { | 1865 | { |
1864 | return NULL; | 1866 | return NULL; |
1865 | } | 1867 | } |
@@ -2001,6 +2003,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, | |||
2001 | unsigned long pages_reclaimed = 0; | 2003 | unsigned long pages_reclaimed = 0; |
2002 | unsigned long did_some_progress; | 2004 | unsigned long did_some_progress; |
2003 | struct task_struct *p = current; | 2005 | struct task_struct *p = current; |
2006 | bool sync_migration = false; | ||
2004 | 2007 | ||
2005 | /* | 2008 | /* |
2006 | * In the slowpath, we sanity check order to avoid ever trying to | 2009 | * In the slowpath, we sanity check order to avoid ever trying to |
@@ -2063,14 +2066,19 @@ rebalance: | |||
2063 | if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL)) | 2066 | if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL)) |
2064 | goto nopage; | 2067 | goto nopage; |
2065 | 2068 | ||
2066 | /* Try direct compaction */ | 2069 | /* |
2070 | * Try direct compaction. The first pass is asynchronous. Subsequent | ||
2071 | * attempts after direct reclaim are synchronous | ||
2072 | */ | ||
2067 | page = __alloc_pages_direct_compact(gfp_mask, order, | 2073 | page = __alloc_pages_direct_compact(gfp_mask, order, |
2068 | zonelist, high_zoneidx, | 2074 | zonelist, high_zoneidx, |
2069 | nodemask, | 2075 | nodemask, |
2070 | alloc_flags, preferred_zone, | 2076 | alloc_flags, preferred_zone, |
2071 | migratetype, &did_some_progress); | 2077 | migratetype, &did_some_progress, |
2078 | sync_migration); | ||
2072 | if (page) | 2079 | if (page) |
2073 | goto got_pg; | 2080 | goto got_pg; |
2081 | sync_migration = true; | ||
2074 | 2082 | ||
2075 | /* Try direct reclaim and then allocating */ | 2083 | /* Try direct reclaim and then allocating */ |
2076 | page = __alloc_pages_direct_reclaim(gfp_mask, order, | 2084 | page = __alloc_pages_direct_reclaim(gfp_mask, order, |
@@ -2134,7 +2142,8 @@ rebalance: | |||
2134 | zonelist, high_zoneidx, | 2142 | zonelist, high_zoneidx, |
2135 | nodemask, | 2143 | nodemask, |
2136 | alloc_flags, preferred_zone, | 2144 | alloc_flags, preferred_zone, |
2137 | migratetype, &did_some_progress); | 2145 | migratetype, &did_some_progress, |
2146 | sync_migration); | ||
2138 | if (page) | 2147 | if (page) |
2139 | goto got_pg; | 2148 | goto got_pg; |
2140 | } | 2149 | } |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 10ebd74a423c..8320d115c85d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -2377,7 +2377,8 @@ loop_again: | |||
2377 | * would ordinarily call try_to_compact_pages() | 2377 | * would ordinarily call try_to_compact_pages() |
2378 | */ | 2378 | */ |
2379 | if (sc.order > PAGE_ALLOC_COSTLY_ORDER) | 2379 | if (sc.order > PAGE_ALLOC_COSTLY_ORDER) |
2380 | compact_zone_order(zone, sc.order, sc.gfp_mask); | 2380 | compact_zone_order(zone, sc.order, sc.gfp_mask, |
2381 | false); | ||
2381 | 2382 | ||
2382 | if (!zone_watermark_ok_safe(zone, order, | 2383 | if (!zone_watermark_ok_safe(zone, order, |
2383 | high_wmark_pages(zone), end_zone, 0)) { | 2384 | high_wmark_pages(zone), end_zone, 0)) { |