When isolating/unisolating some pageblock, we add/sub number of pages returend by move_freepages_block() to calculate number of freepage. But, this value is invalid for calculation, because it means number of pages in buddy list of that migratetype rather than number of *moved* pages. So number of freepage could be incorrect more and more whenever calling these functions. And, there is one more counting problem on __test_page_isolated_in_pageblock(). move_freepages() is called, but missed to fixup number of freepage. I think that counting should be done in move_freepages(), otherwise, another future user to this function also missed to fixup number of freepage again. Now, we have proper infrastructure, get_onbuddy_migratetype(), which can be used to get current migratetype of buddy list. So fix this situation. Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@xxxxxxx> --- mm/page_alloc.c | 37 ++++++++++++++++++++++++++++++++++--- mm/page_isolation.c | 12 +++--------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d9fb8bb..80c9bd8 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -465,6 +465,33 @@ static inline void clear_page_guard(struct zone *zone, struct page *page, unsigned int order, int migratetype) {} #endif +static inline void count_freepage_nr(struct page *page, int order, + int *nr_isolate, int *nr_cma, int *nr_others) +{ + int nr = 1 << order; + int migratetype = get_onbuddy_migratetype(page); + + if (is_migrate_isolate(migratetype)) + *nr_isolate += nr; + else if (is_migrate_cma(migratetype)) + *nr_cma += nr; + else + *nr_others += nr; +} + +static void fixup_freepage_nr(struct zone *zone, int migratetype, + int nr_isolate, int nr_cma, int nr_others) +{ + int nr_free = nr_cma + nr_others; + + if (is_migrate_isolate(migratetype)) { + __mod_zone_page_state(zone, NR_FREE_PAGES, -nr_free); + __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, -nr_cma); + } else { + __mod_zone_freepage_state(zone, nr_isolate, migratetype); + } +} + static inline void set_page_order(struct page *page, unsigned int order, int migratetype) { @@ -619,6 +646,7 @@ static inline void __free_one_page(struct page *page, buddy = page + (buddy_idx - page_idx); if (!page_is_buddy(page, buddy, order)) break; + /* * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page, * merge with it and move up one order. @@ -1062,7 +1090,7 @@ int move_freepages(struct zone *zone, { struct page *page; unsigned long order; - int pages_moved = 0; + int nr_pages = 0, nr_isolate = 0, nr_cma = 0, nr_others = 0; #ifndef CONFIG_HOLES_IN_ZONE /* @@ -1090,14 +1118,17 @@ int move_freepages(struct zone *zone, } order = page_order(page); + count_freepage_nr(page, order, + &nr_isolate, &nr_cma, &nr_others); list_move(&page->lru, &zone->free_area[order].free_list[migratetype]); set_onbuddy_migratetype(page, migratetype); page += 1 << order; - pages_moved += 1 << order; + nr_pages += 1 << order; } - return pages_moved; + fixup_freepage_nr(zone, migratetype, nr_isolate, nr_cma, nr_others); + return nr_pages; } int move_freepages_block(struct zone *zone, struct page *page, diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 6e4e86b..62676de 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -56,14 +56,9 @@ int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages) out: if (!ret) { - unsigned long nr_pages; - int migratetype = get_pageblock_migratetype(page); - set_pageblock_migratetype(page, MIGRATE_ISOLATE); zone->nr_isolate_pageblock++; - nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE); - - __mod_zone_freepage_state(zone, -nr_pages, migratetype); + move_freepages_block(zone, page, MIGRATE_ISOLATE); } spin_unlock_irqrestore(&zone->lock, flags); @@ -75,14 +70,13 @@ out: void unset_migratetype_isolate(struct page *page, unsigned migratetype) { struct zone *zone; - unsigned long flags, nr_pages; + unsigned long flags; zone = page_zone(page); spin_lock_irqsave(&zone->lock, flags); if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) goto out; - nr_pages = move_freepages_block(zone, page, migratetype); - __mod_zone_freepage_state(zone, nr_pages, migratetype); + move_freepages_block(zone, page, migratetype); set_pageblock_migratetype(page, migratetype); zone->nr_isolate_pageblock--; out: -- 1.7.9.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>