Skip to content

Commit c574bbe

Browse files
yhuang-inteltorvalds
authored andcommitted
NUMA balancing: optimize page placement for memory tiering system
With the advent of various new memory types, some machines will have multiple types of memory, e.g. DRAM and PMEM (persistent memory). The memory subsystem of these machines can be called memory tiering system, because the performance of the different types of memory are usually different. In such system, because of the memory accessing pattern changing etc, some pages in the slow memory may become hot globally. So in this patch, the NUMA balancing mechanism is enhanced to optimize the page placement among the different memory types according to hot/cold dynamically. In a typical memory tiering system, there are CPUs, fast memory and slow memory in each physical NUMA node. The CPUs and the fast memory will be put in one logical node (called fast memory node), while the slow memory will be put in another (faked) logical node (called slow memory node). That is, the fast memory is regarded as local while the slow memory is regarded as remote. So it's possible for the recently accessed pages in the slow memory node to be promoted to the fast memory node via the existing NUMA balancing mechanism. The original NUMA balancing mechanism will stop to migrate pages if the free memory of the target node becomes below the high watermark. This is a reasonable policy if there's only one memory type. But this makes the original NUMA balancing mechanism almost do not work to optimize page placement among different memory types. Details are as follows. It's the common cases that the working-set size of the workload is larger than the size of the fast memory nodes. Otherwise, it's unnecessary to use the slow memory at all. So, there are almost always no enough free pages in the fast memory nodes, so that the globally hot pages in the slow memory node cannot be promoted to the fast memory node. To solve the issue, we have 2 choices as follows, a. Ignore the free pages watermark checking when promoting hot pages from the slow memory node to the fast memory node. This will create some memory pressure in the fast memory node, thus trigger the memory reclaiming. So that, the cold pages in the fast memory node will be demoted to the slow memory node. b. Define a new watermark called wmark_promo which is higher than wmark_high, and have kswapd reclaiming pages until free pages reach such watermark. The scenario is as follows: when we want to promote hot-pages from a slow memory to a fast memory, but fast memory's free pages would go lower than high watermark with such promotion, we wake up kswapd with wmark_promo watermark in order to demote cold pages and free us up some space. So, next time we want to promote hot-pages we might have a chance of doing so. The choice "a" may create high memory pressure in the fast memory node. If the memory pressure of the workload is high, the memory pressure may become so high that the memory allocation latency of the workload is influenced, e.g. the direct reclaiming may be triggered. The choice "b" works much better at this aspect. If the memory pressure of the workload is high, the hot pages promotion will stop earlier because its allocation watermark is higher than that of the normal memory allocation. So in this patch, choice "b" is implemented. A new zone watermark (WMARK_PROMO) is added. Which is larger than the high watermark and can be controlled via watermark_scale_factor. In addition to the original page placement optimization among sockets, the NUMA balancing mechanism is extended to be used to optimize page placement according to hot/cold among different memory types. So the sysctl user space interface (numa_balancing) is extended in a backward compatible way as follow, so that the users can enable/disable these functionality individually. The sysctl is converted from a Boolean value to a bits field. The definition of the flags is, - 0: NUMA_BALANCING_DISABLED - 1: NUMA_BALANCING_NORMAL - 2: NUMA_BALANCING_MEMORY_TIERING We have tested the patch with the pmbench memory accessing benchmark with the 80:20 read/write ratio and the Gauss access address distribution on a 2 socket Intel server with Optane DC Persistent Memory Model. The test results shows that the pmbench score can improve up to 95.9%. Thanks Andrew Morton to help fix the document format error. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: "Huang, Ying" <[email protected]> Tested-by: Baolin Wang <[email protected]> Reviewed-by: Baolin Wang <[email protected]> Acked-by: Johannes Weiner <[email protected]> Reviewed-by: Oscar Salvador <[email protected]> Reviewed-by: Yang Shi <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Zi Yan <[email protected]> Cc: Wei Xu <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: zhongjiang-ali <[email protected]> Cc: Randy Dunlap <[email protected]> Cc: Feng Tang <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent e39bb6b commit c574bbe

File tree

8 files changed

+70
-18
lines changed

8 files changed

+70
-18
lines changed

Documentation/admin-guide/sysctl/kernel.rst

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -595,16 +595,23 @@ Documentation/admin-guide/kernel-parameters.rst).
595595
numa_balancing
596596
==============
597597

598-
Enables/disables automatic page fault based NUMA memory
599-
balancing. Memory is moved automatically to nodes
600-
that access it often.
598+
Enables/disables and configures automatic page fault based NUMA memory
599+
balancing. Memory is moved automatically to nodes that access it often.
600+
The value to set can be the result of ORing the following:
601601

602-
Enables/disables automatic NUMA memory balancing. On NUMA machines, there
603-
is a performance penalty if remote memory is accessed by a CPU. When this
604-
feature is enabled the kernel samples what task thread is accessing memory
605-
by periodically unmapping pages and later trapping a page fault. At the
606-
time of the page fault, it is determined if the data being accessed should
607-
be migrated to a local memory node.
602+
= =================================
603+
0 NUMA_BALANCING_DISABLED
604+
1 NUMA_BALANCING_NORMAL
605+
2 NUMA_BALANCING_MEMORY_TIERING
606+
= =================================
607+
608+
Or NUMA_BALANCING_NORMAL to optimize page placement among different
609+
NUMA nodes to reduce remote accessing. On NUMA machines, there is a
610+
performance penalty if remote memory is accessed by a CPU. When this
611+
feature is enabled the kernel samples what task thread is accessing
612+
memory by periodically unmapping pages and later trapping a page
613+
fault. At the time of the page fault, it is determined if the data
614+
being accessed should be migrated to a local memory node.
608615

609616
The unmapping of pages and trapping faults incur additional overhead that
610617
ideally is offset by improved memory locality but there is no universal
@@ -615,6 +622,10 @@ faults may be controlled by the `numa_balancing_scan_period_min_ms,
615622
numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms,
616623
numa_balancing_scan_size_mb`_, and numa_balancing_settle_count sysctls.
617624

625+
Or NUMA_BALANCING_MEMORY_TIERING to optimize page placement among
626+
different types of memory (represented as different NUMA nodes) to
627+
place the hot pages in the fast memory. This is implemented based on
628+
unmapping and page fault too.
618629

619630
numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms, numa_balancing_scan_size_mb
620631
===============================================================================================================================

include/linux/mmzone.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ enum zone_watermarks {
353353
WMARK_MIN,
354354
WMARK_LOW,
355355
WMARK_HIGH,
356+
WMARK_PROMO,
356357
NR_WMARK
357358
};
358359

include/linux/sched/sysctl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ enum sched_tunable_scaling {
2323
SCHED_TUNABLESCALING_END,
2424
};
2525

26+
#define NUMA_BALANCING_DISABLED 0x0
27+
#define NUMA_BALANCING_NORMAL 0x1
28+
#define NUMA_BALANCING_MEMORY_TIERING 0x2
29+
30+
#ifdef CONFIG_NUMA_BALANCING
31+
extern int sysctl_numa_balancing_mode;
32+
#else
33+
#define sysctl_numa_balancing_mode 0
34+
#endif
35+
2636
/*
2737
* control realtime throttling:
2838
*

kernel/sched/core.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4279,21 +4279,32 @@ DEFINE_STATIC_KEY_FALSE(sched_numa_balancing);
42794279

42804280
#ifdef CONFIG_NUMA_BALANCING
42814281

4282-
void set_numabalancing_state(bool enabled)
4282+
int sysctl_numa_balancing_mode;
4283+
4284+
static void __set_numabalancing_state(bool enabled)
42834285
{
42844286
if (enabled)
42854287
static_branch_enable(&sched_numa_balancing);
42864288
else
42874289
static_branch_disable(&sched_numa_balancing);
42884290
}
42894291

4292+
void set_numabalancing_state(bool enabled)
4293+
{
4294+
if (enabled)
4295+
sysctl_numa_balancing_mode = NUMA_BALANCING_NORMAL;
4296+
else
4297+
sysctl_numa_balancing_mode = NUMA_BALANCING_DISABLED;
4298+
__set_numabalancing_state(enabled);
4299+
}
4300+
42904301
#ifdef CONFIG_PROC_SYSCTL
42914302
int sysctl_numa_balancing(struct ctl_table *table, int write,
42924303
void *buffer, size_t *lenp, loff_t *ppos)
42934304
{
42944305
struct ctl_table t;
42954306
int err;
4296-
int state = static_branch_likely(&sched_numa_balancing);
4307+
int state = sysctl_numa_balancing_mode;
42974308

42984309
if (write && !capable(CAP_SYS_ADMIN))
42994310
return -EPERM;
@@ -4303,8 +4314,10 @@ int sysctl_numa_balancing(struct ctl_table *table, int write,
43034314
err = proc_dointvec_minmax(&t, write, buffer, lenp, ppos);
43044315
if (err < 0)
43054316
return err;
4306-
if (write)
4307-
set_numabalancing_state(state);
4317+
if (write) {
4318+
sysctl_numa_balancing_mode = state;
4319+
__set_numabalancing_state(state);
4320+
}
43084321
return err;
43094322
}
43104323
#endif

kernel/sysctl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1696,7 +1696,7 @@ static struct ctl_table kern_table[] = {
16961696
.mode = 0644,
16971697
.proc_handler = sysctl_numa_balancing,
16981698
.extra1 = SYSCTL_ZERO,
1699-
.extra2 = SYSCTL_ONE,
1699+
.extra2 = SYSCTL_FOUR,
17001700
},
17011701
#endif /* CONFIG_NUMA_BALANCING */
17021702
{

mm/migrate.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include <linux/oom.h>
5252
#include <linux/memory.h>
5353
#include <linux/random.h>
54+
#include <linux/sched/sysctl.h>
5455

5556
#include <asm/tlbflush.h>
5657

@@ -2031,16 +2032,27 @@ static int numamigrate_isolate_page(pg_data_t *pgdat, struct page *page)
20312032
{
20322033
int page_lru;
20332034
int nr_pages = thp_nr_pages(page);
2035+
int order = compound_order(page);
20342036

2035-
VM_BUG_ON_PAGE(compound_order(page) && !PageTransHuge(page), page);
2037+
VM_BUG_ON_PAGE(order && !PageTransHuge(page), page);
20362038

20372039
/* Do not migrate THP mapped by multiple processes */
20382040
if (PageTransHuge(page) && total_mapcount(page) > 1)
20392041
return 0;
20402042

20412043
/* Avoid migrating to a node that is nearly full */
2042-
if (!migrate_balanced_pgdat(pgdat, nr_pages))
2044+
if (!migrate_balanced_pgdat(pgdat, nr_pages)) {
2045+
int z;
2046+
2047+
if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING))
2048+
return 0;
2049+
for (z = pgdat->nr_zones - 1; z >= 0; z--) {
2050+
if (populated_zone(pgdat->node_zones + z))
2051+
break;
2052+
}
2053+
wakeup_kswapd(pgdat->node_zones + z, 0, order, ZONE_MOVABLE);
20432054
return 0;
2055+
}
20442056

20452057
if (isolate_lru_page(page))
20462058
return 0;

mm/page_alloc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8441,7 +8441,8 @@ static void __setup_per_zone_wmarks(void)
84418441

84428442
zone->watermark_boost = 0;
84438443
zone->_watermark[WMARK_LOW] = min_wmark_pages(zone) + tmp;
8444-
zone->_watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2;
8444+
zone->_watermark[WMARK_HIGH] = low_wmark_pages(zone) + tmp;
8445+
zone->_watermark[WMARK_PROMO] = high_wmark_pages(zone) + tmp;
84458446

84468447
spin_unlock_irqrestore(&zone->lock, flags);
84478448
}

mm/vmscan.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
#include <linux/swapops.h>
5858
#include <linux/balloon_compaction.h>
59+
#include <linux/sched/sysctl.h>
5960

6061
#include "internal.h"
6162

@@ -3895,7 +3896,10 @@ static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)
38953896
if (!managed_zone(zone))
38963897
continue;
38973898

3898-
mark = high_wmark_pages(zone);
3899+
if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
3900+
mark = wmark_pages(zone, WMARK_PROMO);
3901+
else
3902+
mark = high_wmark_pages(zone);
38993903
if (zone_watermark_ok_safe(zone, order, mark, highest_zoneidx))
39003904
return true;
39013905
}

0 commit comments

Comments
 (0)