mm: Don't hog the CPU and zone lock in rmqueue_bulk()

There is noticeable scheduling latency and heavy zone lock contention
stemming from rmqueue_bulk's single hold of the zone lock while doing
its work, as seen with the preemptoff tracer. There's no actual need for
rmqueue_bulk() to hold the zone lock the entire time; it only does so
for supposed efficiency. As such, we can relax the zone lock and even
reschedule when IRQs are enabled in order to keep the scheduling delays
and zone lock contention at bay. Forward progress is still guaranteed,
as the zone lock can only be relaxed after page removal.

With this change, rmqueue_bulk() no longer appears as a serious offender
in the preemptoff tracer, and system latency is noticeably improved.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
fourteen
Sultan Alsawaf 3 years ago committed by Jenna
parent 490d43491e
commit 8198b5b46e
  1. 23
      mm/page_alloc.c

@ -2406,15 +2406,16 @@ static inline struct page *__rmqueue_cma(struct zone *zone, unsigned int order)
#endif #endif
/* /*
* Obtain a specified number of elements from the buddy allocator, all under * Obtain a specified number of elements from the buddy allocator, and relax the
* a single hold of the lock, for efficiency. Add them to the supplied list. * zone lock when needed. Add them to the supplied list. Returns the number of
* Returns the number of new pages which were placed at *list. * new pages which were placed at *list.
*/ */
static int rmqueue_bulk(struct zone *zone, unsigned int order, static int rmqueue_bulk(struct zone *zone, unsigned int order,
unsigned long count, struct list_head *list, unsigned long count, struct list_head *list,
int migratetype, bool cold) int migratetype, bool cold)
{ {
int i, alloced = 0; const bool can_resched = !preempt_count() && !irqs_disabled();
int i, alloced = 0, last_mod = 0;
spin_lock(&zone->lock); spin_lock(&zone->lock);
for (i = 0; i < count; ++i) { for (i = 0; i < count; ++i) {
@ -2433,6 +2434,18 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
if (unlikely(page == NULL)) if (unlikely(page == NULL))
break; break;
/* Reschedule and ease the contention on the lock if needed */
if (i + 1 < count && ((can_resched && need_resched()) ||
spin_needbreak(&zone->lock))) {
__mod_zone_page_state(zone, NR_FREE_PAGES,
-((i + 1 - last_mod) << order));
last_mod = i + 1;
spin_unlock(&zone->lock);
if (can_resched)
cond_resched();
spin_lock(&zone->lock);
}
if (unlikely(check_pcp_refill(page))) if (unlikely(check_pcp_refill(page)))
continue; continue;
@ -2462,7 +2475,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
* on i. Do not confuse with 'alloced' which is the number of * on i. Do not confuse with 'alloced' which is the number of
* pages added to the pcp list. * pages added to the pcp list.
*/ */
__mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order)); __mod_zone_page_state(zone, NR_FREE_PAGES, -((i - last_mod) << order));
spin_unlock(&zone->lock); spin_unlock(&zone->lock);
return alloced; return alloced;
} }

Loading…
Cancel
Save