On 10/2/25 22:46, Joshua Hahn wrote:
> It is possible for pcp->count - pcp->high to exceed pcp->batch by a lot.
> When this happens, we should perform batching to ensure that
> free_pcppages_bulk isn't called with too many pages to free at once and
> starve out other threads that need the pcp lock.
... or the zone lock.
> Since we are still only freeing the difference between the initial
> pcp->count and pcp->high values, there should be no change to how many
> pages are freed.
>
> Suggested-by: Chris Mason <clm@fb.com>
> Suggested-by: Andrew Morton <akpm@linux-foundation.org>
> Co-developed-by: Johannes Weiner <hannes@cmpxchg.org>
> Signed-off-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
> ---
> mm/page_alloc.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index b9fc357d2d48..f525f197c5fd 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -2559,7 +2559,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
> */
> bool decay_pcp_high(struct zone *zone, struct per_cpu_pages *pcp)
> {
> - int high_min, to_drain, batch;
> + int high_min, to_drain, to_drain_batched, batch;
> bool todo = false;
>
> high_min = READ_ONCE(pcp->high_min);
> @@ -2577,11 +2577,14 @@ bool decay_pcp_high(struct zone *zone, struct per_cpu_pages *pcp)
> }
>
> to_drain = pcp->count - pcp->high;
> - if (to_drain > 0) {
> + while (to_drain > 0) {
> + to_drain_batched = min(to_drain, batch);
> spin_lock(&pcp->lock);
> - free_pcppages_bulk(zone, to_drain, pcp, 0);
> + free_pcppages_bulk(zone, to_drain_batched, pcp, 0);
> spin_unlock(&pcp->lock);
> todo = true;
> +
> + to_drain -= to_drain_batched;
> }
>
> return todo;