diff options
-rw-r--r-- | mm/internal.h | 25 | ||||
-rw-r--r-- | mm/page_alloc.c | 41 | ||||
-rw-r--r-- | mm/page_isolation.c | 41 |
3 files changed, 78 insertions, 29 deletions
diff --git a/mm/internal.h b/mm/internal.h index 829304090b90..a4f90ba7068e 100644 --- a/mm/internal.h +++ b/mm/internal.h | |||
@@ -108,6 +108,31 @@ extern pmd_t *mm_find_pmd(struct mm_struct *mm, unsigned long address); | |||
108 | /* | 108 | /* |
109 | * in mm/page_alloc.c | 109 | * in mm/page_alloc.c |
110 | */ | 110 | */ |
111 | |||
112 | /* | ||
113 | * Locate the struct page for both the matching buddy in our | ||
114 | * pair (buddy1) and the combined O(n+1) page they form (page). | ||
115 | * | ||
116 | * 1) Any buddy B1 will have an order O twin B2 which satisfies | ||
117 | * the following equation: | ||
118 | * B2 = B1 ^ (1 << O) | ||
119 | * For example, if the starting buddy (buddy2) is #8 its order | ||
120 | * 1 buddy is #10: | ||
121 | * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10 | ||
122 | * | ||
123 | * 2) Any buddy B will have an order O+1 parent P which | ||
124 | * satisfies the following equation: | ||
125 | * P = B & ~(1 << O) | ||
126 | * | ||
127 | * Assumption: *_mem_map is contiguous at least up to MAX_ORDER | ||
128 | */ | ||
129 | static inline unsigned long | ||
130 | __find_buddy_index(unsigned long page_idx, unsigned int order) | ||
131 | { | ||
132 | return page_idx ^ (1 << order); | ||
133 | } | ||
134 | |||
135 | extern int __isolate_free_page(struct page *page, unsigned int order); | ||
111 | extern void __free_pages_bootmem(struct page *page, unsigned int order); | 136 | extern void __free_pages_bootmem(struct page *page, unsigned int order); |
112 | extern void prep_compound_page(struct page *page, unsigned long order); | 137 | extern void prep_compound_page(struct page *page, unsigned long order); |
113 | #ifdef CONFIG_MEMORY_FAILURE | 138 | #ifdef CONFIG_MEMORY_FAILURE |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9f689f16b5aa..fd11b913779e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -467,29 +467,6 @@ static inline void rmv_page_order(struct page *page) | |||
467 | } | 467 | } |
468 | 468 | ||
469 | /* | 469 | /* |
470 | * Locate the struct page for both the matching buddy in our | ||
471 | * pair (buddy1) and the combined O(n+1) page they form (page). | ||
472 | * | ||
473 | * 1) Any buddy B1 will have an order O twin B2 which satisfies | ||
474 | * the following equation: | ||
475 | * B2 = B1 ^ (1 << O) | ||
476 | * For example, if the starting buddy (buddy2) is #8 its order | ||
477 | * 1 buddy is #10: | ||
478 | * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10 | ||
479 | * | ||
480 | * 2) Any buddy B will have an order O+1 parent P which | ||
481 | * satisfies the following equation: | ||
482 | * P = B & ~(1 << O) | ||
483 | * | ||
484 | * Assumption: *_mem_map is contiguous at least up to MAX_ORDER | ||
485 | */ | ||
486 | static inline unsigned long | ||
487 | __find_buddy_index(unsigned long page_idx, unsigned int order) | ||
488 | { | ||
489 | return page_idx ^ (1 << order); | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * This function checks whether a page is free && is the buddy | 470 | * This function checks whether a page is free && is the buddy |
494 | * we can do coalesce a page and its buddy if | 471 | * we can do coalesce a page and its buddy if |
495 | * (a) the buddy is not in a hole && | 472 | * (a) the buddy is not in a hole && |
@@ -569,6 +546,7 @@ static inline void __free_one_page(struct page *page, | |||
569 | unsigned long combined_idx; | 546 | unsigned long combined_idx; |
570 | unsigned long uninitialized_var(buddy_idx); | 547 | unsigned long uninitialized_var(buddy_idx); |
571 | struct page *buddy; | 548 | struct page *buddy; |
549 | int max_order = MAX_ORDER; | ||
572 | 550 | ||
573 | VM_BUG_ON(!zone_is_initialized(zone)); | 551 | VM_BUG_ON(!zone_is_initialized(zone)); |
574 | 552 | ||
@@ -577,15 +555,24 @@ static inline void __free_one_page(struct page *page, | |||
577 | return; | 555 | return; |
578 | 556 | ||
579 | VM_BUG_ON(migratetype == -1); | 557 | VM_BUG_ON(migratetype == -1); |
580 | if (!is_migrate_isolate(migratetype)) | 558 | if (is_migrate_isolate(migratetype)) { |
559 | /* | ||
560 | * We restrict max order of merging to prevent merge | ||
561 | * between freepages on isolate pageblock and normal | ||
562 | * pageblock. Without this, pageblock isolation | ||
563 | * could cause incorrect freepage accounting. | ||
564 | */ | ||
565 | max_order = min(MAX_ORDER, pageblock_order + 1); | ||
566 | } else { | ||
581 | __mod_zone_freepage_state(zone, 1 << order, migratetype); | 567 | __mod_zone_freepage_state(zone, 1 << order, migratetype); |
568 | } | ||
582 | 569 | ||
583 | page_idx = pfn & ((1 << MAX_ORDER) - 1); | 570 | page_idx = pfn & ((1 << max_order) - 1); |
584 | 571 | ||
585 | VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page); | 572 | VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page); |
586 | VM_BUG_ON_PAGE(bad_range(zone, page), page); | 573 | VM_BUG_ON_PAGE(bad_range(zone, page), page); |
587 | 574 | ||
588 | while (order < MAX_ORDER-1) { | 575 | while (order < max_order - 1) { |
589 | buddy_idx = __find_buddy_index(page_idx, order); | 576 | buddy_idx = __find_buddy_index(page_idx, order); |
590 | buddy = page + (buddy_idx - page_idx); | 577 | buddy = page + (buddy_idx - page_idx); |
591 | if (!page_is_buddy(page, buddy, order)) | 578 | if (!page_is_buddy(page, buddy, order)) |
@@ -1486,7 +1473,7 @@ void split_page(struct page *page, unsigned int order) | |||
1486 | } | 1473 | } |
1487 | EXPORT_SYMBOL_GPL(split_page); | 1474 | EXPORT_SYMBOL_GPL(split_page); |
1488 | 1475 | ||
1489 | static int __isolate_free_page(struct page *page, unsigned int order) | 1476 | int __isolate_free_page(struct page *page, unsigned int order) |
1490 | { | 1477 | { |
1491 | unsigned long watermark; | 1478 | unsigned long watermark; |
1492 | struct zone *zone; | 1479 | struct zone *zone; |
diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 1fa4a4d72ecc..c8778f7e208e 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c | |||
@@ -76,17 +76,54 @@ void unset_migratetype_isolate(struct page *page, unsigned migratetype) | |||
76 | { | 76 | { |
77 | struct zone *zone; | 77 | struct zone *zone; |
78 | unsigned long flags, nr_pages; | 78 | unsigned long flags, nr_pages; |
79 | struct page *isolated_page = NULL; | ||
80 | unsigned int order; | ||
81 | unsigned long page_idx, buddy_idx; | ||
82 | struct page *buddy; | ||
79 | 83 | ||
80 | zone = page_zone(page); | 84 | zone = page_zone(page); |
81 | spin_lock_irqsave(&zone->lock, flags); | 85 | spin_lock_irqsave(&zone->lock, flags); |
82 | if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) | 86 | if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) |
83 | goto out; | 87 | goto out; |
84 | nr_pages = move_freepages_block(zone, page, migratetype); | 88 | |
85 | __mod_zone_freepage_state(zone, nr_pages, migratetype); | 89 | /* |
90 | * Because freepage with more than pageblock_order on isolated | ||
91 | * pageblock is restricted to merge due to freepage counting problem, | ||
92 | * it is possible that there is free buddy page. | ||
93 | * move_freepages_block() doesn't care of merge so we need other | ||
94 | * approach in order to merge them. Isolation and free will make | ||
95 | * these pages to be merged. | ||
96 | */ | ||
97 | if (PageBuddy(page)) { | ||
98 | order = page_order(page); | ||
99 | if (order >= pageblock_order) { | ||
100 | page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1); | ||
101 | buddy_idx = __find_buddy_index(page_idx, order); | ||
102 | buddy = page + (buddy_idx - page_idx); | ||
103 | |||
104 | if (!is_migrate_isolate_page(buddy)) { | ||
105 | __isolate_free_page(page, order); | ||
106 | set_page_refcounted(page); | ||
107 | isolated_page = page; | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * If we isolate freepage with more than pageblock_order, there | ||
114 | * should be no freepage in the range, so we could avoid costly | ||
115 | * pageblock scanning for freepage moving. | ||
116 | */ | ||
117 | if (!isolated_page) { | ||
118 | nr_pages = move_freepages_block(zone, page, migratetype); | ||
119 | __mod_zone_freepage_state(zone, nr_pages, migratetype); | ||
120 | } | ||
86 | set_pageblock_migratetype(page, migratetype); | 121 | set_pageblock_migratetype(page, migratetype); |
87 | zone->nr_isolate_pageblock--; | 122 | zone->nr_isolate_pageblock--; |
88 | out: | 123 | out: |
89 | spin_unlock_irqrestore(&zone->lock, flags); | 124 | spin_unlock_irqrestore(&zone->lock, flags); |
125 | if (isolated_page) | ||
126 | __free_pages(isolated_page, order); | ||
90 | } | 127 | } |
91 | 128 | ||
92 | static inline struct page * | 129 | static inline struct page * |