aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorJoonsoo Kim <iamjoonsoo.kim@lge.com>2014-12-12 19:56:04 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-13 15:42:48 -0500
commit61cf5febdf6664fa2db86727aa5ab42110fa98a8 (patch)
treec3784f9a7c61222ef0e27b441e0216b198a86ca9 /mm
parent48c96a3685795e52903e60c7ee115e5e22e7d640 (diff)
mm/page_owner: correct owner information for early allocated pages
Extended memory to store page owner information is initialized some time later than that page allocator starts. Until initialization, many pages can be allocated and they have no owner information. This make debugging using page owner harder, so some fixup will be helpful. This patch fixes up this situation by setting fake owner information immediately after page extension is initialized. Information doesn't tell the right owner, but, at least, it can tell whether page is allocated or not, more correctly. On my testing, this patch catches 13343 early allocated pages, although they are mostly allocated from page extension feature. Anyway, after then, there is no page left that it is allocated and has no page owner flag. Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Minchan Kim <minchan@kernel.org> Cc: Dave Hansen <dave@sr71.net> Cc: Michal Nazarewicz <mina86@mina86.com> Cc: Jungsoo Son <jungsoo.son@lge.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.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_owner.c93
1 files changed, 91 insertions, 2 deletions
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 85eec7ea6735..9ab4a9b5bc09 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -10,6 +10,8 @@
10static bool page_owner_disabled = true; 10static bool page_owner_disabled = true;
11bool page_owner_inited __read_mostly; 11bool page_owner_inited __read_mostly;
12 12
13static void init_early_allocated_pages(void);
14
13static int early_page_owner_param(char *buf) 15static int early_page_owner_param(char *buf)
14{ 16{
15 if (!buf) 17 if (!buf)
@@ -36,6 +38,7 @@ static void init_page_owner(void)
36 return; 38 return;
37 39
38 page_owner_inited = true; 40 page_owner_inited = true;
41 init_early_allocated_pages();
39} 42}
40 43
41struct page_ext_operations page_owner_ops = { 44struct page_ext_operations page_owner_ops = {
@@ -184,8 +187,8 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
184 page_ext = lookup_page_ext(page); 187 page_ext = lookup_page_ext(page);
185 188
186 /* 189 /*
187 * Pages allocated before initialization of page_owner are 190 * Some pages could be missed by concurrent allocation or free,
188 * non-buddy and have no page_owner info. 191 * because we don't hold the zone lock.
189 */ 192 */
190 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) 193 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
191 continue; 194 continue;
@@ -199,6 +202,92 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
199 return 0; 202 return 0;
200} 203}
201 204
205static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone)
206{
207 struct page *page;
208 struct page_ext *page_ext;
209 unsigned long pfn = zone->zone_start_pfn, block_end_pfn;
210 unsigned long end_pfn = pfn + zone->spanned_pages;
211 unsigned long count = 0;
212
213 /* Scan block by block. First and last block may be incomplete */
214 pfn = zone->zone_start_pfn;
215
216 /*
217 * Walk the zone in pageblock_nr_pages steps. If a page block spans
218 * a zone boundary, it will be double counted between zones. This does
219 * not matter as the mixed block count will still be correct
220 */
221 for (; pfn < end_pfn; ) {
222 if (!pfn_valid(pfn)) {
223 pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES);
224 continue;
225 }
226
227 block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
228 block_end_pfn = min(block_end_pfn, end_pfn);
229
230 page = pfn_to_page(pfn);
231
232 for (; pfn < block_end_pfn; pfn++) {
233 if (!pfn_valid_within(pfn))
234 continue;
235
236 page = pfn_to_page(pfn);
237
238 /*
239 * We are safe to check buddy flag and order, because
240 * this is init stage and only single thread runs.
241 */
242 if (PageBuddy(page)) {
243 pfn += (1UL << page_order(page)) - 1;
244 continue;
245 }
246
247 if (PageReserved(page))
248 continue;
249
250 page_ext = lookup_page_ext(page);
251
252 /* Maybe overraping zone */
253 if (test_bit(PAGE_EXT_OWNER, &page_ext->flags))
254 continue;
255
256 /* Found early allocated page */
257 set_page_owner(page, 0, 0);
258 count++;
259 }
260 }
261
262 pr_info("Node %d, zone %8s: page owner found early allocated %lu pages\n",
263 pgdat->node_id, zone->name, count);
264}
265
266static void init_zones_in_node(pg_data_t *pgdat)
267{
268 struct zone *zone;
269 struct zone *node_zones = pgdat->node_zones;
270 unsigned long flags;
271
272 for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
273 if (!populated_zone(zone))
274 continue;
275
276 spin_lock_irqsave(&zone->lock, flags);
277 init_pages_in_zone(pgdat, zone);
278 spin_unlock_irqrestore(&zone->lock, flags);
279 }
280}
281
282static void init_early_allocated_pages(void)
283{
284 pg_data_t *pgdat;
285
286 drain_all_pages(NULL);
287 for_each_online_pgdat(pgdat)
288 init_zones_in_node(pgdat);
289}
290
202static const struct file_operations proc_page_owner_operations = { 291static const struct file_operations proc_page_owner_operations = {
203 .read = read_page_owner, 292 .read = read_page_owner,
204}; 293};