summaryrefslogtreecommitdiffstats
path: root/mm/page_alloc.c
diff options
context:
space:
mode:
authorJoonsoo Kim <iamjoonsoo.kim@lge.com>2016-03-15 17:57:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-15 19:55:16 -0400
commit7cf91a98e607c2f935dbcc177d70011e95b8faff (patch)
tree8a57a26127dc9c96059ceedebc2cf13e5d124e3c /mm/page_alloc.c
parente1409c325fdc1fef7b3d8025c51892355f065d15 (diff)
mm/compaction: speed up pageblock_pfn_to_page() when zone is contiguous
There is a performance drop report due to hugepage allocation and in there half of cpu time are spent on pageblock_pfn_to_page() in compaction [1]. In that workload, compaction is triggered to make hugepage but most of pageblocks are un-available for compaction due to pageblock type and skip bit so compaction usually fails. Most costly operations in this case is to find valid pageblock while scanning whole zone range. To check if pageblock is valid to compact, valid pfn within pageblock is required and we can obtain it by calling pageblock_pfn_to_page(). This function checks whether pageblock is in a single zone and return valid pfn if possible. Problem is that we need to check it every time before scanning pageblock even if we re-visit it and this turns out to be very expensive in this workload. Although we have no way to skip this pageblock check in the system where hole exists at arbitrary position, we can use cached value for zone continuity and just do pfn_to_page() in the system where hole doesn't exist. This optimization considerably speeds up in above workload. Before vs After Max: 1096 MB/s vs 1325 MB/s Min: 635 MB/s 1015 MB/s Avg: 899 MB/s 1194 MB/s Avg is improved by roughly 30% [2]. [1]: http://www.spinics.net/lists/linux-mm/msg97378.html [2]: https://lkml.org/lkml/2015/12/9/23 [akpm@linux-foundation.org: don't forget to restore zone->contiguous on error path, per Vlastimil] Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@lge.com> Reported-by: Aaron Lu <aaron.lu@intel.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Tested-by: Aaron Lu <aaron.lu@intel.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Rik van Riel <riel@redhat.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/page_alloc.c')
-rw-r--r--mm/page_alloc.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 50897dcaefdb..c46b75d14b6f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1128,6 +1128,75 @@ void __init __free_pages_bootmem(struct page *page, unsigned long pfn,
1128 return __free_pages_boot_core(page, pfn, order); 1128 return __free_pages_boot_core(page, pfn, order);
1129} 1129}
1130 1130
1131/*
1132 * Check that the whole (or subset of) a pageblock given by the interval of
1133 * [start_pfn, end_pfn) is valid and within the same zone, before scanning it
1134 * with the migration of free compaction scanner. The scanners then need to
1135 * use only pfn_valid_within() check for arches that allow holes within
1136 * pageblocks.
1137 *
1138 * Return struct page pointer of start_pfn, or NULL if checks were not passed.
1139 *
1140 * It's possible on some configurations to have a setup like node0 node1 node0
1141 * i.e. it's possible that all pages within a zones range of pages do not
1142 * belong to a single zone. We assume that a border between node0 and node1
1143 * can occur within a single pageblock, but not a node0 node1 node0
1144 * interleaving within a single pageblock. It is therefore sufficient to check
1145 * the first and last page of a pageblock and avoid checking each individual
1146 * page in a pageblock.
1147 */
1148struct page *__pageblock_pfn_to_page(unsigned long start_pfn,
1149 unsigned long end_pfn, struct zone *zone)
1150{
1151 struct page *start_page;
1152 struct page *end_page;
1153
1154 /* end_pfn is one past the range we are checking */
1155 end_pfn--;
1156
1157 if (!pfn_valid(start_pfn) || !pfn_valid(end_pfn))
1158 return NULL;
1159
1160 start_page = pfn_to_page(start_pfn);
1161
1162 if (page_zone(start_page) != zone)
1163 return NULL;
1164
1165 end_page = pfn_to_page(end_pfn);
1166
1167 /* This gives a shorter code than deriving page_zone(end_page) */
1168 if (page_zone_id(start_page) != page_zone_id(end_page))
1169 return NULL;
1170
1171 return start_page;
1172}
1173
1174void set_zone_contiguous(struct zone *zone)
1175{
1176 unsigned long block_start_pfn = zone->zone_start_pfn;
1177 unsigned long block_end_pfn;
1178
1179 block_end_pfn = ALIGN(block_start_pfn + 1, pageblock_nr_pages);
1180 for (; block_start_pfn < zone_end_pfn(zone);
1181 block_start_pfn = block_end_pfn,
1182 block_end_pfn += pageblock_nr_pages) {
1183
1184 block_end_pfn = min(block_end_pfn, zone_end_pfn(zone));
1185
1186 if (!__pageblock_pfn_to_page(block_start_pfn,
1187 block_end_pfn, zone))
1188 return;
1189 }
1190
1191 /* We confirm that there is no hole */
1192 zone->contiguous = true;
1193}
1194
1195void clear_zone_contiguous(struct zone *zone)
1196{
1197 zone->contiguous = false;
1198}
1199
1131#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT 1200#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
1132static void __init deferred_free_range(struct page *page, 1201static void __init deferred_free_range(struct page *page,
1133 unsigned long pfn, int nr_pages) 1202 unsigned long pfn, int nr_pages)
@@ -1278,9 +1347,13 @@ free_range:
1278 pgdat_init_report_one_done(); 1347 pgdat_init_report_one_done();
1279 return 0; 1348 return 0;
1280} 1349}
1350#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
1281 1351
1282void __init page_alloc_init_late(void) 1352void __init page_alloc_init_late(void)
1283{ 1353{
1354 struct zone *zone;
1355
1356#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
1284 int nid; 1357 int nid;
1285 1358
1286 /* There will be num_node_state(N_MEMORY) threads */ 1359 /* There will be num_node_state(N_MEMORY) threads */
@@ -1294,8 +1367,11 @@ void __init page_alloc_init_late(void)
1294 1367
1295 /* Reinit limits that are based on free pages after the kernel is up */ 1368 /* Reinit limits that are based on free pages after the kernel is up */
1296 files_maxfiles_init(); 1369 files_maxfiles_init();
1370#endif
1371
1372 for_each_populated_zone(zone)
1373 set_zone_contiguous(zone);
1297} 1374}
1298#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
1299 1375
1300#ifdef CONFIG_CMA 1376#ifdef CONFIG_CMA
1301/* Free whole pageblock and set its migration type to MIGRATE_CMA. */ 1377/* Free whole pageblock and set its migration type to MIGRATE_CMA. */