When lazy unaccepted memory is active, pages on zone->unaccepted_pages are counted in NR_FREE_PAGES (MIGRATE_MOVABLE) as well as NR_UNACCEPTED. Without accounting for NR_UNACCEPTED in free page reporting watermarks, page_reporting_process_zone() and __isolate_free_page() treat unaccepted pages as already-accepted free buddy pages and can over-drain accepted free lists.
Account for zone_page_state(zone, NR_UNACCEPTED) in __isolate_free_page(), page_reporting_cycle(), and page_reporting_process_zone() (using promo_wmark_pages(zone) for free page reporting watermarks and skipping non-mergeable migratetypes when has_unaccepted_memory() is active), and enforce unaccepted_memory_order() as the minimum page_reporting_order. Signed-off-by: Kevin Loughlin <[email protected]> --- mm/page_alloc.c | 3 +++ mm/page_reporting.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c10b77304633..541b2eaf34a4 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3183,6 +3183,9 @@ int __isolate_free_page(struct page *page, unsigned int order) * exists. */ watermark = zone->_watermark[WMARK_MIN] + (1UL << order); +#ifdef CONFIG_UNACCEPTED_MEMORY + watermark += zone_page_state(zone, NR_UNACCEPTED); +#endif if (!zone_watermark_ok(zone, 0, watermark, 0, ALLOC_CMA)) return 0; } diff --git a/mm/page_reporting.c b/mm/page_reporting.c index de587be17801..adacdb015b55 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -17,11 +17,14 @@ unsigned int page_reporting_order = PAGE_REPORTING_ORDER_UNSPECIFIED; static int page_order_update_notify(const char *val, const struct kernel_param *kp) { + unsigned int min_order = has_unaccepted_memory() ? + unaccepted_memory_order() : 0; + /* * If param is set beyond this limit, order is set to default * pageblock_order value */ - return param_set_uint_minmax(val, kp, 0, MAX_PAGE_ORDER); + return param_set_uint_minmax(val, kp, min_order, MAX_PAGE_ORDER); } static const struct kernel_param_ops page_reporting_param_ops = { @@ -206,6 +209,21 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, /* Attempt to pull page from list and place in scatterlist */ if (*offset) { +#ifdef CONFIG_UNACCEPTED_MEMORY + if (has_unaccepted_memory() || + !list_empty(&zone->unaccepted_pages)) { + unsigned long watermark; + + watermark = promo_wmark_pages(zone) + + zone_page_state(zone, NR_UNACCEPTED) + + (1UL << order); + if (!zone_watermark_ok(zone, 0, watermark, 0, + ALLOC_CMA)) { + next = page; + break; + } + } +#endif if (!__isolate_free_page(page, order)) { next = page; break; @@ -275,6 +293,12 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev, /* Generate minimum watermark to be able to guarantee progress */ watermark = low_wmark_pages(zone) + (prdev->capacity << page_reporting_order); +#ifdef CONFIG_UNACCEPTED_MEMORY + if (has_unaccepted_memory() || !list_empty(&zone->unaccepted_pages)) + watermark = promo_wmark_pages(zone) + + zone_page_state(zone, NR_UNACCEPTED) + + (prdev->capacity << page_reporting_order); +#endif /* * Cancel request if insufficient free memory or if we failed @@ -289,6 +313,11 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev, /* We do not pull pages from the isolate free list */ if (is_migrate_isolate(mt)) continue; +#ifdef CONFIG_UNACCEPTED_MEMORY + if (has_unaccepted_memory() && + !migratetype_is_mergeable(mt)) + continue; +#endif err = page_reporting_cycle(prdev, zone, order, mt, sgl, &offset); @@ -384,6 +413,9 @@ int page_reporting_register(struct page_reporting_dev_info *prdev) else page_reporting_order = pageblock_order; } + if (has_unaccepted_memory() && + page_reporting_order < unaccepted_memory_order()) + page_reporting_order = unaccepted_memory_order(); if (!prdev->capacity || prdev->capacity > PAGE_REPORTING_CAPACITY) prdev->capacity = PAGE_REPORTING_CAPACITY; -- 2.56.0.307.g5fe7bd680c-goog
