diff options
Diffstat (limited to 'mm/page_isolation.c')
-rw-r--r-- | mm/page_isolation.c | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/mm/page_isolation.c b/mm/page_isolation.c index d1473b2e9481..c8778f7e208e 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c | |||
@@ -60,6 +60,7 @@ out: | |||
60 | int migratetype = get_pageblock_migratetype(page); | 60 | int migratetype = get_pageblock_migratetype(page); |
61 | 61 | ||
62 | set_pageblock_migratetype(page, MIGRATE_ISOLATE); | 62 | set_pageblock_migratetype(page, MIGRATE_ISOLATE); |
63 | zone->nr_isolate_pageblock++; | ||
63 | nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE); | 64 | nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE); |
64 | 65 | ||
65 | __mod_zone_freepage_state(zone, -nr_pages, migratetype); | 66 | __mod_zone_freepage_state(zone, -nr_pages, migratetype); |
@@ -75,16 +76,54 @@ void unset_migratetype_isolate(struct page *page, unsigned migratetype) | |||
75 | { | 76 | { |
76 | struct zone *zone; | 77 | struct zone *zone; |
77 | 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; | ||
78 | 83 | ||
79 | zone = page_zone(page); | 84 | zone = page_zone(page); |
80 | spin_lock_irqsave(&zone->lock, flags); | 85 | spin_lock_irqsave(&zone->lock, flags); |
81 | if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) | 86 | if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) |
82 | goto out; | 87 | goto out; |
83 | nr_pages = move_freepages_block(zone, page, migratetype); | 88 | |
84 | __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 | } | ||
85 | set_pageblock_migratetype(page, migratetype); | 121 | set_pageblock_migratetype(page, migratetype); |
122 | zone->nr_isolate_pageblock--; | ||
86 | out: | 123 | out: |
87 | spin_unlock_irqrestore(&zone->lock, flags); | 124 | spin_unlock_irqrestore(&zone->lock, flags); |
125 | if (isolated_page) | ||
126 | __free_pages(isolated_page, order); | ||
88 | } | 127 | } |
89 | 128 | ||
90 | static inline struct page * | 129 | static inline struct page * |