| From: Alexander Halbuer <halbuer@sra.uni-hannover.de> |
| Subject: mm: reduce lock contention of pcp buffer refill |
| Date: Wed, 1 Feb 2023 17:25:49 +0100 |
| |
| rmqueue_bulk() batches the allocation of multiple elements to refill the |
| per-CPU buffers into a single hold of the zone lock. Each element is |
| allocated and checked using check_pcp_refill(). The check touches every |
| related struct page which is especially expensive for higher order |
| allocations (huge pages). |
| |
| This patch reduces the time holding the lock by moving the check out of |
| the critical section similar to rmqueue_buddy() which allocates a single |
| element. |
| |
| Measurements of parallel allocation-heavy workloads show a reduction of |
| the average huge page allocation latency of 50 percent for two cores and |
| nearly 90 percent for 24 cores. |
| |
| Link: https://lkml.kernel.org/r/20230201162549.68384-1-halbuer@sra.uni-hannover.de |
| Signed-off-by: Alexander Halbuer <halbuer@sra.uni-hannover.de> |
| Cc: Mel Gorman <mgorman@techsingularity.net> |
| Cc: Vlastimil Babka <vbabka@suse.cz> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| mm/page_alloc.c | 22 ++++++++++++++++++---- |
| 1 file changed, 18 insertions(+), 4 deletions(-) |
| |
| --- a/mm/page_alloc.c~mm-reduce-lock-contention-of-pcp-buffer-refill |
| +++ a/mm/page_alloc.c |
| @@ -3138,6 +3138,8 @@ static int rmqueue_bulk(struct zone *zon |
| { |
| unsigned long flags; |
| int i, allocated = 0; |
| + struct list_head *prev_tail = list->prev; |
| + struct page *pos, *n; |
| |
| spin_lock_irqsave(&zone->lock, flags); |
| for (i = 0; i < count; ++i) { |
| @@ -3146,9 +3148,6 @@ static int rmqueue_bulk(struct zone *zon |
| if (unlikely(page == NULL)) |
| break; |
| |
| - if (unlikely(check_pcp_refill(page, order))) |
| - continue; |
| - |
| /* |
| * Split buddy pages returned by expand() are received here in |
| * physical page order. The page is added to the tail of |
| @@ -3160,7 +3159,6 @@ static int rmqueue_bulk(struct zone *zon |
| * pages are ordered properly. |
| */ |
| list_add_tail(&page->pcp_list, list); |
| - allocated++; |
| if (is_migrate_cma(get_pcppage_migratetype(page))) |
| __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, |
| -(1 << order)); |
| @@ -3174,6 +3172,22 @@ static int rmqueue_bulk(struct zone *zon |
| */ |
| __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order)); |
| spin_unlock_irqrestore(&zone->lock, flags); |
| + |
| + /* |
| + * Pages are appended to the pcp list without checking to reduce the |
| + * time holding the zone lock. Checking the appended pages happens right |
| + * after the critical section while still holding the pcp lock. |
| + */ |
| + pos = list_first_entry(prev_tail, struct page, pcp_list); |
| + list_for_each_entry_safe_from(pos, n, list, pcp_list) { |
| + if (unlikely(check_pcp_refill(pos, order))) { |
| + list_del(&pos->pcp_list); |
| + continue; |
| + } |
| + |
| + allocated++; |
| + } |
| + |
| return allocated; |
| } |
| |
| _ |