summaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorVlastimil Babka <vbabka@suse.cz>2017-05-08 18:54:37 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-08 20:15:09 -0400
commit3bc48f96cf11ce8699e419d5e47ae0d456403274 (patch)
tree6aa29830c56d296169668187a5cae80cd602f58d /mm
parent228d7e33903040a0b9dd9a5ee9b3a49c538c0613 (diff)
mm, page_alloc: split smallest stolen page in fallback
The __rmqueue_fallback() function is called when there's no free page of requested migratetype, and we need to steal from a different one. There are various heuristics to make this event infrequent and reduce permanent fragmentation. The main one is to try stealing from a pageblock that has the most free pages, and possibly steal them all at once and convert the whole pageblock. Precise searching for such pageblock would be expensive, so instead the heuristics walks the free lists from MAX_ORDER down to requested order and assumes that the block with highest-order free page is likely to also have the most free pages in total. Chances are that together with the highest-order page, we steal also pages of lower orders from the same block. But then we still split the highest order page. This is wasteful and can contribute to fragmentation instead of avoiding it. This patch thus changes __rmqueue_fallback() to just steal the page(s) and put them on the freelist of the requested migratetype, and only report whether it was successful. Then we pick (and eventually split) the smallest page with __rmqueue_smallest(). This all happens under zone lock, so nobody can steal it from us in the process. This should reduce fragmentation due to fallbacks. At worst we are only stealing a single highest-order page and waste some cycles by moving it between lists and then removing it, but fallback is not exactly hot path so that should not be a concern. As a side benefit the patch removes some duplicate code by reusing __rmqueue_smallest(). [vbabka@suse.cz: fix endless loop in the modified __rmqueue()] Link: http://lkml.kernel.org/r/59d71b35-d556-4fc9-ee2e-1574259282fd@suse.cz Link: http://lkml.kernel.org/r/20170307131545.28577-4-vbabka@suse.cz Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Acked-by: Mel Gorman <mgorman@techsingularity.net> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: David Rientjes <rientjes@google.com> 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/page_alloc.c62
1 files changed, 37 insertions, 25 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2c25de46c58f..2f1118b4dda4 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1948,23 +1948,44 @@ static bool can_steal_fallback(unsigned int order, int start_mt)
1948 * use it's pages as requested migratetype in the future. 1948 * use it's pages as requested migratetype in the future.
1949 */ 1949 */
1950static void steal_suitable_fallback(struct zone *zone, struct page *page, 1950static void steal_suitable_fallback(struct zone *zone, struct page *page,
1951 int start_type) 1951 int start_type, bool whole_block)
1952{ 1952{
1953 unsigned int current_order = page_order(page); 1953 unsigned int current_order = page_order(page);
1954 struct free_area *area;
1954 int pages; 1955 int pages;
1955 1956
1957 /*
1958 * This can happen due to races and we want to prevent broken
1959 * highatomic accounting.
1960 */
1961 if (is_migrate_highatomic_page(page))
1962 goto single_page;
1963
1956 /* Take ownership for orders >= pageblock_order */ 1964 /* Take ownership for orders >= pageblock_order */
1957 if (current_order >= pageblock_order) { 1965 if (current_order >= pageblock_order) {
1958 change_pageblock_range(page, current_order, start_type); 1966 change_pageblock_range(page, current_order, start_type);
1959 return; 1967 goto single_page;
1960 } 1968 }
1961 1969
1970 /* We are not allowed to try stealing from the whole block */
1971 if (!whole_block)
1972 goto single_page;
1973
1962 pages = move_freepages_block(zone, page, start_type); 1974 pages = move_freepages_block(zone, page, start_type);
1975 /* moving whole block can fail due to zone boundary conditions */
1976 if (!pages)
1977 goto single_page;
1963 1978
1964 /* Claim the whole block if over half of it is free */ 1979 /* Claim the whole block if over half of it is free */
1965 if (pages >= (1 << (pageblock_order-1)) || 1980 if (pages >= (1 << (pageblock_order-1)) ||
1966 page_group_by_mobility_disabled) 1981 page_group_by_mobility_disabled)
1967 set_pageblock_migratetype(page, start_type); 1982 set_pageblock_migratetype(page, start_type);
1983
1984 return;
1985
1986single_page:
1987 area = &zone->free_area[current_order];
1988 list_move(&page->lru, &area->free_list[start_type]);
1968} 1989}
1969 1990
1970/* 1991/*
@@ -2123,8 +2144,13 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
2123 return false; 2144 return false;
2124} 2145}
2125 2146
2126/* Remove an element from the buddy allocator from the fallback list */ 2147/*
2127static inline struct page * 2148 * Try finding a free buddy page on the fallback list and put it on the free
2149 * list of requested migratetype, possibly along with other pages from the same
2150 * block, depending on fragmentation avoidance heuristics. Returns true if
2151 * fallback was found so that __rmqueue_smallest() can grab it.
2152 */
2153static inline bool
2128__rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) 2154__rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype)
2129{ 2155{
2130 struct free_area *area; 2156 struct free_area *area;
@@ -2145,32 +2171,17 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype)
2145 2171
2146 page = list_first_entry(&area->free_list[fallback_mt], 2172 page = list_first_entry(&area->free_list[fallback_mt],
2147 struct page, lru); 2173 struct page, lru);
2148 if (can_steal && !is_migrate_highatomic_page(page))
2149 steal_suitable_fallback(zone, page, start_migratetype);
2150 2174
2151 /* Remove the page from the freelists */ 2175 steal_suitable_fallback(zone, page, start_migratetype,
2152 area->nr_free--; 2176 can_steal);
2153 list_del(&page->lru);
2154 rmv_page_order(page);
2155
2156 expand(zone, page, order, current_order, area,
2157 start_migratetype);
2158 /*
2159 * The pcppage_migratetype may differ from pageblock's
2160 * migratetype depending on the decisions in
2161 * find_suitable_fallback(). This is OK as long as it does not
2162 * differ for MIGRATE_CMA pageblocks. Those can be used as
2163 * fallback only via special __rmqueue_cma_fallback() function
2164 */
2165 set_pcppage_migratetype(page, start_migratetype);
2166 2177
2167 trace_mm_page_alloc_extfrag(page, order, current_order, 2178 trace_mm_page_alloc_extfrag(page, order, current_order,
2168 start_migratetype, fallback_mt); 2179 start_migratetype, fallback_mt);
2169 2180
2170 return page; 2181 return true;
2171 } 2182 }
2172 2183
2173 return NULL; 2184 return false;
2174} 2185}
2175 2186
2176/* 2187/*
@@ -2182,13 +2193,14 @@ static struct page *__rmqueue(struct zone *zone, unsigned int order,
2182{ 2193{
2183 struct page *page; 2194 struct page *page;
2184 2195
2196retry:
2185 page = __rmqueue_smallest(zone, order, migratetype); 2197 page = __rmqueue_smallest(zone, order, migratetype);
2186 if (unlikely(!page)) { 2198 if (unlikely(!page)) {
2187 if (migratetype == MIGRATE_MOVABLE) 2199 if (migratetype == MIGRATE_MOVABLE)
2188 page = __rmqueue_cma_fallback(zone, order); 2200 page = __rmqueue_cma_fallback(zone, order);
2189 2201
2190 if (!page) 2202 if (!page && __rmqueue_fallback(zone, order, migratetype))
2191 page = __rmqueue_fallback(zone, order, migratetype); 2203 goto retry;
2192 } 2204 }
2193 2205
2194 trace_mm_page_alloc_zone_locked(page, order, migratetype); 2206 trace_mm_page_alloc_zone_locked(page, order, migratetype);