linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] memory tiering: hot page selection
@ 2022-04-08  7:12 Huang Ying
  2022-04-08  7:12 ` [PATCH 1/3] memory tiering: hot page selection with hint page fault latency Huang Ying
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Huang Ying @ 2022-04-08  7:12 UTC (permalink / raw)
  To: Peter Zijlstra, Mel Gorman, Andrew Morton
  Cc: linux-mm, linux-kernel, Huang Ying, Michal Hocko, Rik van Riel,
	Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador, Shakeel Butt,
	Zhong Jiang

To optimize page placement in a memory tiering system with NUMA
balancing, the hot pages in the slow memory node need to be
identified.  Essentially, the original NUMA balancing implementation
selects and promote the mostly recently accessed (MRU) pages.  But the
recently accessed pages may be cold.  So in this patchset, we
implement a new hot page identification algorithm based on the latency
between NUMA balancing page table scanning and hint page fault.

And the hot page promotion can incur some overhead in the system.  To
control the overhead a simple promotion rate limit mechanism is
implemented.

The hot threshold used to identify the hot pages is workload dependent
usually.  So we also implemented a hot threshold automatic adjustment
algorithm.  The basic idea is to increase/decrease the hot threshold
to make the number of pages that pass the hot threshold (promote
candidate) near the rate limit.

We used the pmbench memory accessing benchmark tested the patchset on
a 2-socket server system with DRAM and PMEM installed.  The test
results are as follows,

		pmbench score		promote rate
		 (accesses/s)			MB/s
		-------------		------------
base		  146887704.1		       725.6
hot selection     165695601.2		       544.0
rate limit	  162814569.8		       165.2
auto adjustment	  170495294.0                  136.9

From the results above,

With hot page selection patch [1/3], the pmbench score increases about
12.8%, and promote rate (overhead) decreases about 25.0%, compared with
base kernel.

With rate limit patch [2/3], pmbench score decreases about 1.7%, and
promote rate decreases about 69.6%, compared with hot page selection
patch.

With threshold auto adjustment patch [3/3], pmbench score increases
about 4.7%, and promote rate decrease about 17.1%, compared with rate
limit patch.

Best Regards,
Huang, Ying


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] memory tiering: hot page selection with hint page fault latency
  2022-04-08  7:12 [PATCH 0/3] memory tiering: hot page selection Huang Ying
@ 2022-04-08  7:12 ` Huang Ying
  2022-04-14 13:23   ` Jagdish Gediya
  2022-04-08  7:12 ` [PATCH 2/3] memory tiering: rate limit NUMA migration throughput Huang Ying
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Huang Ying @ 2022-04-08  7:12 UTC (permalink / raw)
  To: Peter Zijlstra, Mel Gorman, Andrew Morton
  Cc: linux-mm, linux-kernel, Huang Ying, Michal Hocko, Rik van Riel,
	Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador, Shakeel Butt,
	Zhong Jiang

To optimize page placement in a memory tiering system with NUMA
balancing, the hot pages in the slow memory node need to be
identified.  Essentially, the original NUMA balancing implementation
selects the mostly recently accessed (MRU) pages as the hot pages.
But this isn't a very good algorithm to identify the hot pages.

So, in this patch we implemented a better hot page selection
algorithm.  Which is based on NUMA balancing page table scanning and
hint page fault as follows,

- When the page tables of the processes are scanned to change PTE/PMD
  to be PROT_NONE, the current time is recorded in struct page as scan
  time.

- When the page is accessed, hint page fault will occur.  The scan
  time is gotten from the struct page.  And The hint page fault
  latency is defined as

    hint page fault time - scan time

The shorter the hint page fault latency of a page is, the higher the
probability of their access frequency to be higher.  So the hint page
fault latency is a good estimation of the page hot/cold.

But it's hard to find some extra space in struct page to hold the scan
time.  Fortunately, we can reuse some bits used by the original NUMA
balancing.

NUMA balancing uses some bits in struct page to store the page
accessing CPU and PID (referring to page_cpupid_xchg_last()).  Which
is used by the multi-stage node selection algorithm to avoid to
migrate pages shared accessed by the NUMA nodes back and forth.  But
for pages in the slow memory node, even if they are shared accessed by
multiple NUMA nodes, as long as the pages are hot, they need to be
promoted to the fast memory node.  So the accessing CPU and PID
information are unnecessary for the slow memory pages.  We can reuse
these bits in struct page to record the scan time for them.  For the
fast memory pages, these bits are used as before.

For the hot threshold, the default value is 1 second, which works well
in our performance test.  All pages with hint page fault latency < the
threshold will be considered hot.  A debugfs interface is also
provided to adjust the hot threshold.

The downside of the above method is that the response time to the
workload hot spot changing may be much longer.  For example,

- A previous cold memory area becomes hot

- The hint page fault will be triggered.  But the hint page fault
  latency isn't shorter than the hot threshold.  So the pages will
  not be promoted.

- When the memory area is scanned again, maybe after a scan period,
  the hint page fault latency measured will be shorter than the hot
  threshold and the pages will be promoted.

To mitigate this,

- If there are enough free space in the fast memory node, the hot
  threshold will not be used, all pages will be promoted upon the hint
  page fault for fast response.

- If fast response is more important for system performance, the
  administrator can set a higher hot threshold.

Thanks Zhong Jiang reported and tested the fix for a bug when
disabling memory tiering mode dynamically.

Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Wei Xu <weixugc@google.com>
Cc: osalvador <osalvador@suse.de>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Zhong Jiang <zhongjiang-ali@linux.alibaba.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
---
 include/linux/mm.h   | 30 ++++++++++++++++++
 kernel/sched/debug.c |  1 +
 kernel/sched/fair.c  | 74 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/sched.h |  1 +
 mm/huge_memory.c     | 13 ++++++--
 mm/memory.c          | 11 ++++++-
 mm/migrate.c         | 12 +++++++
 mm/mprotect.c        |  8 ++++-
 8 files changed, 145 insertions(+), 5 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index e34edb775334..455a3d0e699d 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1311,6 +1311,18 @@ static inline int folio_nid(const struct folio *folio)
 }
 
 #ifdef CONFIG_NUMA_BALANCING
+/* page access time bits needs to hold at least 4 seconds */
+#define PAGE_ACCESS_TIME_MIN_BITS	12
+#if LAST_CPUPID_SHIFT < PAGE_ACCESS_TIME_MIN_BITS
+#define PAGE_ACCESS_TIME_BUCKETS				\
+	(PAGE_ACCESS_TIME_MIN_BITS - LAST_CPUPID_SHIFT)
+#else
+#define PAGE_ACCESS_TIME_BUCKETS	0
+#endif
+
+#define PAGE_ACCESS_TIME_MASK				\
+	(LAST_CPUPID_MASK << PAGE_ACCESS_TIME_BUCKETS)
+
 static inline int cpu_pid_to_cpupid(int cpu, int pid)
 {
 	return ((cpu & LAST__CPU_MASK) << LAST__PID_SHIFT) | (pid & LAST__PID_MASK);
@@ -1346,6 +1358,11 @@ static inline bool __cpupid_match_pid(pid_t task_pid, int cpupid)
 	return (task_pid & LAST__PID_MASK) == cpupid_to_pid(cpupid);
 }
 
+static inline bool check_cpupid(int cpupid)
+{
+	return cpupid_to_cpu(cpupid) < nr_cpu_ids;
+}
+
 #define cpupid_match_pid(task, cpupid) __cpupid_match_pid(task->pid, cpupid)
 #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
 static inline int page_cpupid_xchg_last(struct page *page, int cpupid)
@@ -1374,12 +1391,25 @@ static inline void page_cpupid_reset_last(struct page *page)
 	page->flags |= LAST_CPUPID_MASK << LAST_CPUPID_PGSHIFT;
 }
 #endif /* LAST_CPUPID_NOT_IN_PAGE_FLAGS */
+
+static inline int xchg_page_access_time(struct page *page, int time)
+{
+	int last_time;
+
+	last_time = page_cpupid_xchg_last(page, time >> PAGE_ACCESS_TIME_BUCKETS);
+	return last_time << PAGE_ACCESS_TIME_BUCKETS;
+}
 #else /* !CONFIG_NUMA_BALANCING */
 static inline int page_cpupid_xchg_last(struct page *page, int cpupid)
 {
 	return page_to_nid(page); /* XXX */
 }
 
+static inline int xchg_page_access_time(struct page *page, int time)
+{
+	return 0;
+}
+
 static inline int page_cpupid_last(struct page *page)
 {
 	return page_to_nid(page); /* XXX */
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index bb3d63bdf4ae..ad63dbfc54f1 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -333,6 +333,7 @@ static __init int sched_init_debug(void)
 	debugfs_create_u32("scan_period_min_ms", 0644, numa, &sysctl_numa_balancing_scan_period_min);
 	debugfs_create_u32("scan_period_max_ms", 0644, numa, &sysctl_numa_balancing_scan_period_max);
 	debugfs_create_u32("scan_size_mb", 0644, numa, &sysctl_numa_balancing_scan_size);
+	debugfs_create_u32("hot_threshold_ms", 0644, numa, &sysctl_numa_balancing_hot_threshold);
 #endif
 
 	debugfs_create_file("debug", 0444, debugfs_sched, NULL, &sched_debug_fops);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d4bd299d67ab..cb130ea46c71 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1058,6 +1058,9 @@ unsigned int sysctl_numa_balancing_scan_size = 256;
 /* Scan @scan_size MB every @scan_period after an initial @scan_delay in ms */
 unsigned int sysctl_numa_balancing_scan_delay = 1000;
 
+/* The page with hint page fault latency < threshold in ms is considered hot */
+unsigned int sysctl_numa_balancing_hot_threshold = 1000;
+
 struct numa_group {
 	refcount_t refcount;
 
@@ -1400,6 +1403,37 @@ static inline unsigned long group_weight(struct task_struct *p, int nid,
 	return 1000 * faults / total_faults;
 }
 
+static bool pgdat_free_space_enough(struct pglist_data *pgdat)
+{
+	int z;
+	unsigned long enough_mark;
+
+	enough_mark = max(1UL * 1024 * 1024 * 1024 >> PAGE_SHIFT,
+			  pgdat->node_present_pages >> 4);
+	for (z = pgdat->nr_zones - 1; z >= 0; z--) {
+		struct zone *zone = pgdat->node_zones + z;
+
+		if (!populated_zone(zone))
+			continue;
+
+		if (zone_watermark_ok(zone, 0,
+				      high_wmark_pages(zone) + enough_mark,
+				      ZONE_MOVABLE, 0))
+			return true;
+	}
+	return false;
+}
+
+static int numa_hint_fault_latency(struct page *page)
+{
+	int last_time, time;
+
+	time = jiffies_to_msecs(jiffies);
+	last_time = xchg_page_access_time(page, time);
+
+	return (time - last_time) & PAGE_ACCESS_TIME_MASK;
+}
+
 bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 				int src_nid, int dst_cpu)
 {
@@ -1407,9 +1441,38 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 	int dst_nid = cpu_to_node(dst_cpu);
 	int last_cpupid, this_cpupid;
 
+	/*
+	 * The pages in slow memory node should be migrated according
+	 * to hot/cold instead of accessing CPU node.
+	 */
+	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
+	    !node_is_toptier(src_nid)) {
+		struct pglist_data *pgdat;
+		unsigned long latency, th;
+
+		pgdat = NODE_DATA(dst_nid);
+		if (pgdat_free_space_enough(pgdat))
+			return true;
+
+		th = sysctl_numa_balancing_hot_threshold;
+		latency = numa_hint_fault_latency(page);
+		if (latency >= th)
+			return false;
+
+		return true;
+	}
+
 	this_cpupid = cpu_pid_to_cpupid(dst_cpu, current->pid);
 	last_cpupid = page_cpupid_xchg_last(page, this_cpupid);
 
+	/*
+	 * The cpupid may be invalid when NUMA_BALANCING_MEMORY_TIERING
+	 * is disabled dynamically.
+	 */
+	if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
+	    !node_is_toptier(src_nid) && !check_cpupid(last_cpupid))
+		return false;
+
 	/*
 	 * Allow first faults or private faults to migrate immediately early in
 	 * the lifetime of a task. The magic number 4 is based on waiting for
@@ -2642,6 +2705,17 @@ void task_numa_fault(int last_cpupid, int mem_node, int pages, int flags)
 	if (!p->mm)
 		return;
 
+	/*
+	 * NUMA faults statistics are unnecessary for the slow memory node.
+	 *
+	 * And, the cpupid may be invalid when NUMA_BALANCING_MEMORY_TIERING
+	 * is disabled dynamically.
+	 */
+	if (!node_is_toptier(mem_node) &&
+	    (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING ||
+	     !check_cpupid(last_cpupid)))
+		return;
+
 	/* Allocate buffer to track faults on a per-node basis */
 	if (unlikely(!p->numa_faults)) {
 		int size = sizeof(*p->numa_faults) *
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 58263f90c559..86ce60d3d472 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2408,6 +2408,7 @@ extern unsigned int sysctl_numa_balancing_scan_delay;
 extern unsigned int sysctl_numa_balancing_scan_period_min;
 extern unsigned int sysctl_numa_balancing_scan_period_max;
 extern unsigned int sysctl_numa_balancing_scan_size;
+extern unsigned int sysctl_numa_balancing_hot_threshold;
 #endif
 
 #ifdef CONFIG_SCHED_HRTICK
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2fe38212e07c..3a715eeeebb5 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1401,7 +1401,7 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
 	struct page *page;
 	unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
 	int page_nid = NUMA_NO_NODE;
-	int target_nid, last_cpupid = -1;
+	int target_nid, last_cpupid = (-1 & LAST_CPUPID_MASK);
 	bool migrated = false;
 	bool was_writable = pmd_savedwrite(oldpmd);
 	int flags = 0;
@@ -1422,7 +1422,8 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
 		flags |= TNF_NO_GROUP;
 
 	page_nid = page_to_nid(page);
-	last_cpupid = page_cpupid_last(page);
+	if (node_is_toptier(page_nid))
+		last_cpupid = page_cpupid_last(page);
 	target_nid = numa_migrate_prep(page, vma, haddr, page_nid,
 				       &flags);
 
@@ -1740,6 +1741,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 
 	if (prot_numa) {
 		struct page *page;
+		bool toptier;
 		/*
 		 * Avoid trapping faults against the zero page. The read-only
 		 * data is likely to be read-cached on the local CPU and
@@ -1752,13 +1754,18 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 			goto unlock;
 
 		page = pmd_page(*pmd);
+		toptier = node_is_toptier(page_to_nid(page));
 		/*
 		 * Skip scanning top tier node if normal numa
 		 * balancing is disabled
 		 */
 		if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) &&
-		    node_is_toptier(page_to_nid(page)))
+		    toptier)
 			goto unlock;
+
+		if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
+		    !toptier)
+			xchg_page_access_time(page, jiffies_to_msecs(jiffies));
 	}
 	/*
 	 * In case prot_numa, we are under mmap_read_lock(mm). It's critical
diff --git a/mm/memory.c b/mm/memory.c
index b370ed118767..a8ac15ce7a75 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -74,6 +74,7 @@
 #include <linux/perf_event.h>
 #include <linux/ptrace.h>
 #include <linux/vmalloc.h>
+#include <linux/sched/sysctl.h>
 
 #include <trace/events/kmem.h>
 
@@ -4455,8 +4456,16 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
 	if (page_mapcount(page) > 1 && (vma->vm_flags & VM_SHARED))
 		flags |= TNF_SHARED;
 
-	last_cpupid = page_cpupid_last(page);
 	page_nid = page_to_nid(page);
+	/*
+	 * In memory tiering mode, cpupid of slow memory page is used
+	 * to record page access time.  So use default value.
+	 */
+	if ((sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
+	    !node_is_toptier(page_nid))
+		last_cpupid = (-1 & LAST_CPUPID_MASK);
+	else
+		last_cpupid = page_cpupid_last(page);
 	target_nid = numa_migrate_prep(page, vma, vmf->address, page_nid,
 			&flags);
 	if (target_nid == NUMA_NO_NODE) {
diff --git a/mm/migrate.c b/mm/migrate.c
index dc84edfae842..e73f26dfeb38 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -534,6 +534,18 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
 	 * future migrations of this same page.
 	 */
 	cpupid = page_cpupid_xchg_last(&folio->page, -1);
+	/*
+	 * If migrate between slow and fast memory node, reset cpupid,
+	 * because that is used to record page access time in slow
+	 * memory node
+	 */
+	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) {
+		bool f_toptier = node_is_toptier(page_to_nid(&folio->page));
+		bool t_toptier = node_is_toptier(page_to_nid(&newfolio->page));
+
+		if (f_toptier != t_toptier)
+			cpupid = -1;
+	}
 	page_cpupid_xchg_last(&newfolio->page, cpupid);
 
 	folio_migrate_ksm(newfolio, folio);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index b69ce7a7b2b7..e7cb90d84fac 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -85,6 +85,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 			if (prot_numa) {
 				struct page *page;
 				int nid;
+				bool toptier;
 
 				/* Avoid TLB flush if possible */
 				if (pte_protnone(oldpte))
@@ -114,14 +115,19 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 				nid = page_to_nid(page);
 				if (target_node == nid)
 					continue;
+				toptier = node_is_toptier(nid);
 
 				/*
 				 * Skip scanning top tier node if normal numa
 				 * balancing is disabled
 				 */
 				if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) &&
-				    node_is_toptier(nid))
+				    toptier)
 					continue;
+				if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
+				    !toptier)
+					xchg_page_access_time(page,
+						jiffies_to_msecs(jiffies));
 			}
 
 			oldpte = ptep_modify_prot_start(vma, addr, pte);
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] memory tiering: rate limit NUMA migration throughput
  2022-04-08  7:12 [PATCH 0/3] memory tiering: hot page selection Huang Ying
  2022-04-08  7:12 ` [PATCH 1/3] memory tiering: hot page selection with hint page fault latency Huang Ying
@ 2022-04-08  7:12 ` Huang Ying
  2022-04-08  7:12 ` [PATCH 3/3] memory tiering: adjust hot threshold automatically Huang Ying
  2022-04-09  4:07 ` [PATCH 0/3] memory tiering: hot page selection Andrew Morton
  3 siblings, 0 replies; 8+ messages in thread
From: Huang Ying @ 2022-04-08  7:12 UTC (permalink / raw)
  To: Peter Zijlstra, Mel Gorman, Andrew Morton
  Cc: linux-mm, linux-kernel, Huang Ying, Michal Hocko, Rik van Riel,
	Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador, Shakeel Butt

In NUMA balancing memory tiering mode, the hot slow memory pages could
be promoted to the fast memory node via NUMA balancing.  But this
incurs some overhead too.  So that sometimes the workload performance
may be hurt.  To avoid too much disturbing to the workload in these
situations, we should make it possible to rate limit the promotion
throughput.

So, in this patch, we implement a simple rate limit algorithm as
follows.  The number of the candidate pages to be promoted to the fast
memory node via NUMA balancing is counted, if the count exceeds the
limit specified by the users, the NUMA balancing promotion will be
stopped until the next second.

A new sysctl knob kernel.numa_balancing_rate_limit_mbps is added for
the users to specify the limit.

TODO: Add ABI document for new sysctl knob.

Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Wei Xu <weixugc@google.com>
Cc: osalvador <osalvador@suse.de>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
---
 include/linux/mmzone.h       |  5 +++++
 include/linux/sched/sysctl.h |  1 +
 kernel/sched/fair.c          | 28 ++++++++++++++++++++++++++--
 kernel/sysctl.c              |  8 ++++++++
 mm/vmstat.c                  |  1 +
 5 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 962b14d403e8..e9b4767619bc 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -224,6 +224,7 @@ enum node_stat_item {
 #endif
 #ifdef CONFIG_NUMA_BALANCING
 	PGPROMOTE_SUCCESS,	/* promote successfully */
+	PGPROMOTE_CANDIDATE,	/* candidate pages to promote */
 #endif
 	NR_VM_NODE_STAT_ITEMS
 };
@@ -915,6 +916,10 @@ typedef struct pglist_data {
 	struct deferred_split deferred_split_queue;
 #endif
 
+#ifdef CONFIG_NUMA_BALANCING
+	unsigned long numa_ts;
+	unsigned long numa_nr_candidate;
+#endif
 	/* Fields commonly accessed by the page reclaim scanner */
 
 	/*
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index c1076b5e17fb..b25ce13e646a 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -29,6 +29,7 @@ enum sched_tunable_scaling {
 
 #ifdef CONFIG_NUMA_BALANCING
 extern int sysctl_numa_balancing_mode;
+extern unsigned int sysctl_numa_balancing_rate_limit;
 #else
 #define sysctl_numa_balancing_mode	0
 #endif
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index cb130ea46c71..a1e7cbfb2647 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1060,6 +1060,11 @@ unsigned int sysctl_numa_balancing_scan_delay = 1000;
 
 /* The page with hint page fault latency < threshold in ms is considered hot */
 unsigned int sysctl_numa_balancing_hot_threshold = 1000;
+/*
+ * Restrict the NUMA migration per second in MB for each target node
+ * if no enough free space in target node
+ */
+unsigned int sysctl_numa_balancing_rate_limit = 65536;
 
 struct numa_group {
 	refcount_t refcount;
@@ -1434,6 +1439,23 @@ static int numa_hint_fault_latency(struct page *page)
 	return (time - last_time) & PAGE_ACCESS_TIME_MASK;
 }
 
+static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
+					    unsigned long rate_limit, int nr)
+{
+	unsigned long nr_candidate;
+	unsigned long now = jiffies, last_ts;
+
+	mod_node_page_state(pgdat, PGPROMOTE_CANDIDATE, nr);
+	nr_candidate = node_page_state(pgdat, PGPROMOTE_CANDIDATE);
+	last_ts = pgdat->numa_ts;
+	if (now > last_ts + HZ &&
+	    cmpxchg(&pgdat->numa_ts, last_ts, now) == last_ts)
+		pgdat->numa_nr_candidate = nr_candidate;
+	if (nr_candidate - pgdat->numa_nr_candidate >= rate_limit)
+		return false;
+	return true;
+}
+
 bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 				int src_nid, int dst_cpu)
 {
@@ -1448,7 +1470,7 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
 	    !node_is_toptier(src_nid)) {
 		struct pglist_data *pgdat;
-		unsigned long latency, th;
+		unsigned long rate_limit, latency, th;
 
 		pgdat = NODE_DATA(dst_nid);
 		if (pgdat_free_space_enough(pgdat))
@@ -1459,7 +1481,9 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 		if (latency >= th)
 			return false;
 
-		return true;
+		rate_limit = sysctl_numa_balancing_rate_limit << (20 - PAGE_SHIFT);
+		return numa_migration_check_rate_limit(pgdat, rate_limit,
+						       thp_nr_pages(page));
 	}
 
 	this_cpupid = cpu_pid_to_cpupid(dst_cpu, current->pid);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 830aaf8ca08e..c2de22027e7c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1698,6 +1698,14 @@ static struct ctl_table kern_table[] = {
 		.extra1		= SYSCTL_ZERO,
 		.extra2		= SYSCTL_FOUR,
 	},
+	{
+		.procname	= "numa_balancing_rate_limit_mbps",
+		.data		= &sysctl_numa_balancing_rate_limit,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+	},
 #endif /* CONFIG_NUMA_BALANCING */
 	{
 		.procname	= "sched_rt_period_us",
diff --git a/mm/vmstat.c b/mm/vmstat.c
index b75b1a64b54c..bbaf732df925 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1245,6 +1245,7 @@ const char * const vmstat_text[] = {
 #endif
 #ifdef CONFIG_NUMA_BALANCING
 	"pgpromote_success",
+	"pgpromote_candidate",
 #endif
 
 	/* enum writeback_stat_item counters */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] memory tiering: adjust hot threshold automatically
  2022-04-08  7:12 [PATCH 0/3] memory tiering: hot page selection Huang Ying
  2022-04-08  7:12 ` [PATCH 1/3] memory tiering: hot page selection with hint page fault latency Huang Ying
  2022-04-08  7:12 ` [PATCH 2/3] memory tiering: rate limit NUMA migration throughput Huang Ying
@ 2022-04-08  7:12 ` Huang Ying
  2022-04-09  4:07 ` [PATCH 0/3] memory tiering: hot page selection Andrew Morton
  3 siblings, 0 replies; 8+ messages in thread
From: Huang Ying @ 2022-04-08  7:12 UTC (permalink / raw)
  To: Peter Zijlstra, Mel Gorman, Andrew Morton
  Cc: linux-mm, linux-kernel, Huang Ying, Michal Hocko, Rik van Riel,
	Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador, Shakeel Butt

The promotion hot threshold may be workload dependent.  So in this
patch, a method to adjust the hot threshold automatically is
implemented.  The basic idea is to control the number of the candidate
promotion pages to match the promotion rate limit.  If the hint page
fault latency of a page is less than the hot threshold, we will try to
promote the page, and the page is called the candidate promotion page.

If the number of the candidate promotion pages in the statistics
interval is much more than the promotion rate limit, the hot threshold
will be decreased to reduce the number of the candidate promotion
pages.  Otherwise, the hot threshold will be increased to increase the
number of the candidate promotion pages.

To make the above method works, in each statistics interval, the total
number of the pages to check (on which the hint page faults occur) and
the hot/cold distribution need to be stable.  Because the page tables
are scanned linearly in NUMA balancing, but the hot/cold distribution
isn't uniform along the address usually, the statistics interval
should be larger than the NUMA balancing scan period.  So in the
patch, the max scan period is used as statistics interval and it works
well in our tests.

Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Wei Xu <weixugc@google.com>
Cc: osalvador <osalvador@suse.de>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
---
 include/linux/mmzone.h       |  3 ++
 include/linux/sched/sysctl.h |  2 ++
 kernel/sched/core.c          | 15 +++++++++
 kernel/sched/fair.c          | 62 +++++++++++++++++++++++++++++++++---
 4 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index e9b4767619bc..4abf1af20372 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -919,6 +919,9 @@ typedef struct pglist_data {
 #ifdef CONFIG_NUMA_BALANCING
 	unsigned long numa_ts;
 	unsigned long numa_nr_candidate;
+	unsigned long numa_threshold_ts;
+	unsigned long numa_threshold_nr_candidate;
+	unsigned long numa_threshold;
 #endif
 	/* Fields commonly accessed by the page reclaim scanner */
 
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index b25ce13e646a..58c1937ba09f 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -67,6 +67,8 @@ int sysctl_sched_uclamp_handler(struct ctl_table *table, int write,
 		void *buffer, size_t *lenp, loff_t *ppos);
 int sysctl_numa_balancing(struct ctl_table *table, int write, void *buffer,
 		size_t *lenp, loff_t *ppos);
+int sysctl_numa_balancing_threshold(struct ctl_table *table, int write, void *buffer,
+		size_t *lenp, loff_t *ppos);
 int sysctl_schedstats(struct ctl_table *table, int write, void *buffer,
 		size_t *lenp, loff_t *ppos);
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d575b4914925..179cc4c16e24 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4364,6 +4364,18 @@ void set_numabalancing_state(bool enabled)
 }
 
 #ifdef CONFIG_PROC_SYSCTL
+static void reset_memory_tiering(void)
+{
+	struct pglist_data *pgdat;
+
+	for_each_online_pgdat(pgdat) {
+		pgdat->numa_threshold = 0;
+		pgdat->numa_threshold_nr_candidate =
+			node_page_state(pgdat, PGPROMOTE_CANDIDATE);
+		pgdat->numa_threshold_ts = jiffies;
+	}
+}
+
 int sysctl_numa_balancing(struct ctl_table *table, int write,
 			  void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -4380,6 +4392,9 @@ int sysctl_numa_balancing(struct ctl_table *table, int write,
 	if (err < 0)
 		return err;
 	if (write) {
+		if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
+		    (state & NUMA_BALANCING_MEMORY_TIERING))
+			reset_memory_tiering();
 		sysctl_numa_balancing_mode = state;
 		__set_numabalancing_state(state);
 	}
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a1e7cbfb2647..a01216d95bd7 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1456,6 +1456,54 @@ static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
 	return true;
 }
 
+int sysctl_numa_balancing_threshold(struct ctl_table *table, int write, void *buffer,
+		size_t *lenp, loff_t *ppos)
+{
+	int err;
+	struct pglist_data *pgdat;
+
+	if (write && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	if (err < 0 || !write)
+		return err;
+
+	for_each_online_pgdat(pgdat)
+		pgdat->numa_threshold = 0;
+
+	return err;
+}
+
+#define NUMA_MIGRATION_ADJUST_STEPS	16
+
+static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
+					    unsigned long rate_limit,
+					    unsigned long ref_th)
+{
+	unsigned long now = jiffies, last_th_ts, th_period;
+	unsigned long unit_th, th;
+	unsigned long nr_cand, ref_cand, diff_cand;
+
+	th_period = msecs_to_jiffies(sysctl_numa_balancing_scan_period_max);
+	last_th_ts = pgdat->numa_threshold_ts;
+	if (now > last_th_ts + th_period &&
+	    cmpxchg(&pgdat->numa_threshold_ts, last_th_ts, now) == last_th_ts) {
+		ref_cand = rate_limit *
+			sysctl_numa_balancing_scan_period_max / 1000;
+		nr_cand = node_page_state(pgdat, PGPROMOTE_CANDIDATE);
+		diff_cand = nr_cand - pgdat->numa_threshold_nr_candidate;
+		unit_th = ref_th / NUMA_MIGRATION_ADJUST_STEPS;
+		th = pgdat->numa_threshold ? : ref_th;
+		if (diff_cand > ref_cand * 11 / 10)
+			th = max(th - unit_th, unit_th);
+		else if (diff_cand < ref_cand * 9 / 10)
+			th = min(th + unit_th, ref_th);
+		pgdat->numa_threshold_nr_candidate = nr_cand;
+		pgdat->numa_threshold = th;
+	}
+}
+
 bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 				int src_nid, int dst_cpu)
 {
@@ -1470,18 +1518,24 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
 	    !node_is_toptier(src_nid)) {
 		struct pglist_data *pgdat;
-		unsigned long rate_limit, latency, th;
+		unsigned long rate_limit, latency, th, def_th;
 
 		pgdat = NODE_DATA(dst_nid);
-		if (pgdat_free_space_enough(pgdat))
+		if (pgdat_free_space_enough(pgdat)) {
+			/* workload changed, reset hot threshold */
+			pgdat->numa_threshold = 0;
 			return true;
+		}
+
+		def_th = sysctl_numa_balancing_hot_threshold;
+		rate_limit = sysctl_numa_balancing_rate_limit << (20 - PAGE_SHIFT);
+		numa_migration_adjust_threshold(pgdat, rate_limit, def_th);
 
-		th = sysctl_numa_balancing_hot_threshold;
+		th = pgdat->numa_threshold ? : def_th;
 		latency = numa_hint_fault_latency(page);
 		if (latency >= th)
 			return false;
 
-		rate_limit = sysctl_numa_balancing_rate_limit << (20 - PAGE_SHIFT);
 		return numa_migration_check_rate_limit(pgdat, rate_limit,
 						       thp_nr_pages(page));
 	}
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/3] memory tiering: hot page selection
  2022-04-08  7:12 [PATCH 0/3] memory tiering: hot page selection Huang Ying
                   ` (2 preceding siblings ...)
  2022-04-08  7:12 ` [PATCH 3/3] memory tiering: adjust hot threshold automatically Huang Ying
@ 2022-04-09  4:07 ` Andrew Morton
  2022-04-11  8:16   ` ying.huang
  3 siblings, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2022-04-09  4:07 UTC (permalink / raw)
  To: Huang Ying
  Cc: Peter Zijlstra, Mel Gorman, linux-mm, linux-kernel, Michal Hocko,
	Rik van Riel, Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador,
	Shakeel Butt, Zhong Jiang

On Fri,  8 Apr 2022 15:12:19 +0800 Huang Ying <ying.huang@intel.com> wrote:

> To optimize page placement in a memory tiering system with NUMA
> balancing, the hot pages in the slow memory node need to be
> identified.  Essentially, the original NUMA balancing implementation
> selects and promote the mostly recently accessed (MRU) pages.  But the
> recently accessed pages may be cold.

Wait.  What.  How on earth could the most recently accessed page be
considered "cold"?

Changelog needs work, please.

> So in this patchset, we
> implement a new hot page identification algorithm based on the latency
> between NUMA balancing page table scanning and hint page fault.

That sounds reasonable.

> And the hot page promotion can incur some overhead in the system.  To
> control the overhead a simple promotion rate limit mechanism is
> implemented.

That sounds like a hack to fix a problem you just added?  Maybe a
reasonable one..

> The hot threshold used to identify the hot pages is workload dependent
> usually.  So we also implemented a hot threshold automatic adjustment
> algorithm.  The basic idea is to increase/decrease the hot threshold
> to make the number of pages that pass the hot threshold (promote
> candidate) near the rate limit.

hot threshold is tweakable via debugfs.  So it's an unstable interface,
undocumented, may be withdrawn at any time, etc.

Please justify this.  Is it intended that this interface be removed? 
If yes, please describe the plan for this in the changelog.  If no then
this interface should be in sysfs, it should be fully documented in the
kernel tree and it should be fully changelogged (preferably with usage
examples) so that reviewers can best understand what is a permanent
extension to the kernel API which we must maintain for all time.



Does this code build correctly if !LAST_CPUPID_NOT_IN_PAGE_FLAGS?


> TODO: Add ABI document for new sysctl knob.

um, yes.


check_cpupid() is a poor function name.  Check for what?  Liver damage?
A better identifier would be cpupid_valid(), perhaps.  And perhaps it
should be implemented in mm.h.  And even documented.


Oddly, the proposed changes do a decent job of documenting
intra-function changes but a poor job of documenting newly added
functions.  Please review every new function and decide whether it is
so simple and obvious that it can be added to Linux without any inline
description at all.  

pgdat_free_space_enough() and  numa_hint_fault_latency().

numa_migration_check_rate_limit() particularly.  There's that "check"
word again.  Check for what?



The numa_balancing_rate_limit_mbps sysctl.  I assume "b" means "bytes".
That's better than "pages", but still.  Machines vary a lot in how
many bytes they have and in the speed at which they process those
bytes.  Is there some metric which we can use here which at least
partially insulates us from this variability?  So that sysadmins don't
need to set all their machines differently, based upon their size and
speed?


numa_threshold_ts is apparently in units of jiffies.  This was not
documented at the definition site, and it should be.  But jiffies can
vary between machines and configs.  Why add this inter-machine and
inter-config uncertainty/variability when we have include/linux/ktime.h?





^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/3] memory tiering: hot page selection
  2022-04-09  4:07 ` [PATCH 0/3] memory tiering: hot page selection Andrew Morton
@ 2022-04-11  8:16   ` ying.huang
  0 siblings, 0 replies; 8+ messages in thread
From: ying.huang @ 2022-04-11  8:16 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Peter Zijlstra, Mel Gorman, linux-mm, linux-kernel, Michal Hocko,
	Rik van Riel, Dave Hansen, Yang Shi, Zi Yan, Wei Xu, osalvador,
	Shakeel Butt, Zhong Jiang

Hi, Andrew,

Thanks a lots for your comments!

On Fri, 2022-04-08 at 21:07 -0700, Andrew Morton wrote:
> On Fri,  8 Apr 2022 15:12:19 +0800 Huang Ying <ying.huang@intel.com>
> wrote:
> 
> > To optimize page placement in a memory tiering system with NUMA
> > balancing, the hot pages in the slow memory node need to be
> > identified.  Essentially, the original NUMA balancing
> > implementation
> > selects and promote the mostly recently accessed (MRU) pages.  But
> > the
> > recently accessed pages may be cold.
> 
> Wait.  What.  How on earth could the most recently accessed page be
> considered "cold"?
> 
> Changelog needs work, please.

Sorry for confusing.  What I really want to say is,

The pages with low accessing frequency should be kept in slow memory
node.  But these pages will still be accessed, so they may be selected
and migrated to the fast memory node by the original NUMA balancing
implementation upon accessing.

Are the new words better?

> > So in this patchset, we
> > implement a new hot page identification algorithm based on the
> > latency
> > between NUMA balancing page table scanning and hint page fault.
> 
> That sounds reasonable.
> 
> > And the hot page promotion can incur some overhead in the system. 
> > To
> > control the overhead a simple promotion rate limit mechanism is
> > implemented.
> 
> That sounds like a hack to fix a problem you just added?  Maybe a
> reasonable one..

Sorry again for confusing.  Try to reword it as follows,

If there are hot pages in slow memory node and cold pages in fast
memory node, we need to promote/demote hot/cold pages between the fast
and cold memory nodes.

One choice is to promote/demote as fast as possible.  But the CPU
cycles and memory bandwidth consumed by the fast promoting/demoting may
hurt the performance of the workload, e.g., the workload latency.

One way to resolve this issue is to restrict the max promoting/demoting
bandwidth.  It will take longer to finish the promoting/demoting.  But
the workload latency may be better.  This is implemented in this
patchset as the page promotion rate limit mechanism.

> > The hot threshold used to identify the hot pages is workload
> > dependent
> > usually.  So we also implemented a hot threshold automatic
> > adjustment
> > algorithm.  The basic idea is to increase/decrease the hot
> > threshold
> > to make the number of pages that pass the hot threshold (promote
> > candidate) near the rate limit.
> 
> hot threshold is tweakable via debugfs.  So it's an unstable
> interface,
> undocumented, may be withdrawn at any time, etc.
> 
> Please justify this.  Is it intended that this interface be removed? 
> If yes, please describe the plan for this in the changelog.  If no
> then
> this interface should be in sysfs, it should be fully documented in
> the
> kernel tree and it should be fully changelogged (preferably with
> usage
> examples) so that reviewers can best understand what is a permanent
> extension to the kernel API which we must maintain for all time.

Previously, I had added a hot threshold sysfs interface.  But later, I
thought that it's hard for the users to determine right hot threshold
value.  On the other hand, in patch [3/3], we implemented a hot
threshold automatic adjustment algorithm, which make it not so
important to set the hot threshold from the user space.  So I moved the
hot threshold setting interface to debugfs just for users to
experiment.  If it turns out that a formal interface is needed finally,
I will add one.

> Does this code build correctly if !LAST_CPUPID_NOT_IN_PAGE_FLAGS?
> 

I have relied on 0-Day kernel build service to capture the build bug of
more configurations and architectures.  But I just tried i386
architcture, and found that NUMA balancing isn't supported there, so I
cannot build test that for i386 too.  Should I try more?

> > TODO: Add ABI document for new sysctl knob.
> 
> um, yes.
> 

Sure.  Will do that in the next version.

> check_cpupid() is a poor function name.  Check for what?  Liver
> damage?
> A better identifier would be cpupid_valid(), perhaps.  And perhaps it
> should be implemented in mm.h.  And even documented.

Yes.  cpupid_valid() is much better.  I will add document for it in the
next version.

> Oddly, the proposed changes do a decent job of documenting
> intra-function changes but a poor job of documenting newly added
> functions.  Please review every new function and decide whether it is
> so simple and obvious that it can be added to Linux without any
> inline
> description at all.  
> 
> pgdat_free_space_enough() and  numa_hint_fault_latency().

Sure.  Will check all new functions and add document if necessary.

> numa_migration_check_rate_limit() particularly.  There's that "check"
> word again.  Check for what?
> 

The original idea is to check whether the migration is ratelimited. 
Will change this to numa_migration_rate_limit().  It will return true
if migration rate exceed the limit.

> 
> The numa_balancing_rate_limit_mbps sysctl.  I assume "b" means
> "bytes".
> That's better than "pages", but still.  Machines vary a lot in how
> many bytes they have and in the speed at which they process those
> bytes.  Is there some metric which we can use here which at least
> partially insulates us from this variability?  So that sysadmins
> don't
> need to set all their machines differently, based upon their size and
> speed?

Will change the sysctl to numb_balancing_promote_rate_limit_MBps to
avoid possible confusing.

The default rate limit is 64 GB/s, that is, no limitation.  As Mel
Gorman suggested.

To reduce the possible disturbing to the workload, A rule of thumb is
to set it to be 10% of the max slow memory bandwidth.  But we don't
know how to detect that automatically yet.

> numa_threshold_ts is apparently in units of jiffies.  This was not
> documented at the definition site, and it should be.  But jiffies can
> vary between machines and configs.  Why add this inter-machine and
> inter-config uncertainty/variability when we have
> include/linux/ktime.h?
> 

We need to call cmpxchg() on it, so ktime_t may be not the best choice
here.  But I got your idea.  I will use some more general time unit,
e.g. milli-second.

Best Regards,
Huang, Ying




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] memory tiering: hot page selection with hint page fault latency
  2022-04-08  7:12 ` [PATCH 1/3] memory tiering: hot page selection with hint page fault latency Huang Ying
@ 2022-04-14 13:23   ` Jagdish Gediya
  2022-04-15  2:42     ` ying.huang
  0 siblings, 1 reply; 8+ messages in thread
From: Jagdish Gediya @ 2022-04-14 13:23 UTC (permalink / raw)
  To: Huang Ying
  Cc: Peter Zijlstra, Mel Gorman, Andrew Morton, linux-mm,
	linux-kernel, Michal Hocko, Rik van Riel, Dave Hansen, Yang Shi,
	Zi Yan, Wei Xu, osalvador, Shakeel Butt, Zhong Jiang

On Fri, Apr 08, 2022 at 03:12:20PM +0800, Huang Ying wrote:
> To optimize page placement in a memory tiering system with NUMA
> balancing, the hot pages in the slow memory node need to be
> identified.  Essentially, the original NUMA balancing implementation
> selects the mostly recently accessed (MRU) pages as the hot pages.
> But this isn't a very good algorithm to identify the hot pages.
> 
> So, in this patch we implemented a better hot page selection
> algorithm.  Which is based on NUMA balancing page table scanning and
> hint page fault as follows,
> 
> - When the page tables of the processes are scanned to change PTE/PMD
>   to be PROT_NONE, the current time is recorded in struct page as scan
>   time.
> 
> - When the page is accessed, hint page fault will occur.  The scan
>   time is gotten from the struct page.  And The hint page fault
>   latency is defined as
> 
>     hint page fault time - scan time
> 
> The shorter the hint page fault latency of a page is, the higher the
> probability of their access frequency to be higher.  So the hint page
> fault latency is a good estimation of the page hot/cold.
> 
> But it's hard to find some extra space in struct page to hold the scan
> time.  Fortunately, we can reuse some bits used by the original NUMA
> balancing.
> 
> NUMA balancing uses some bits in struct page to store the page
> accessing CPU and PID (referring to page_cpupid_xchg_last()).  Which
> is used by the multi-stage node selection algorithm to avoid to
> migrate pages shared accessed by the NUMA nodes back and forth.  But
> for pages in the slow memory node, even if they are shared accessed by
> multiple NUMA nodes, as long as the pages are hot, they need to be
> promoted to the fast memory node.  So the accessing CPU and PID
> information are unnecessary for the slow memory pages.  We can reuse
> these bits in struct page to record the scan time for them.  For the
> fast memory pages, these bits are used as before.
> 
> For the hot threshold, the default value is 1 second, which works well
> in our performance test.  All pages with hint page fault latency < the
> threshold will be considered hot.  A debugfs interface is also
> provided to adjust the hot threshold.
> 
> The downside of the above method is that the response time to the
> workload hot spot changing may be much longer.  For example,
> 
> - A previous cold memory area becomes hot
> 
> - The hint page fault will be triggered.  But the hint page fault
>   latency isn't shorter than the hot threshold.  So the pages will
>   not be promoted.
> 
> - When the memory area is scanned again, maybe after a scan period,
>   the hint page fault latency measured will be shorter than the hot
>   threshold and the pages will be promoted.
> 
> To mitigate this,
> 
> - If there are enough free space in the fast memory node, the hot
>   threshold will not be used, all pages will be promoted upon the hint
>   page fault for fast response.
> 
> - If fast response is more important for system performance, the
>   administrator can set a higher hot threshold.
> 
> Thanks Zhong Jiang reported and tested the fix for a bug when
> disabling memory tiering mode dynamically.
> 
> Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Michal Hocko <mhocko@suse.com>
> Cc: Rik van Riel <riel@surriel.com>
> Cc: Mel Gorman <mgorman@techsingularity.net>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: Yang Shi <shy828301@gmail.com>
> Cc: Zi Yan <ziy@nvidia.com>
> Cc: Wei Xu <weixugc@google.com>
> Cc: osalvador <osalvador@suse.de>
> Cc: Shakeel Butt <shakeelb@google.com>
> Cc: Zhong Jiang <zhongjiang-ali@linux.alibaba.com>
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-mm@kvack.org
> ---
>  include/linux/mm.h   | 30 ++++++++++++++++++
>  kernel/sched/debug.c |  1 +
>  kernel/sched/fair.c  | 74 ++++++++++++++++++++++++++++++++++++++++++++
>  kernel/sched/sched.h |  1 +
>  mm/huge_memory.c     | 13 ++++++--
>  mm/memory.c          | 11 ++++++-
>  mm/migrate.c         | 12 +++++++
>  mm/mprotect.c        |  8 ++++-
>  8 files changed, 145 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index e34edb775334..455a3d0e699d 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1311,6 +1311,18 @@ static inline int folio_nid(const struct folio *folio)
>  }
>  
>  #ifdef CONFIG_NUMA_BALANCING
> +/* page access time bits needs to hold at least 4 seconds */
> +#define PAGE_ACCESS_TIME_MIN_BITS	12
> +#if LAST_CPUPID_SHIFT < PAGE_ACCESS_TIME_MIN_BITS
> +#define PAGE_ACCESS_TIME_BUCKETS				\
> +	(PAGE_ACCESS_TIME_MIN_BITS - LAST_CPUPID_SHIFT)
> +#else
> +#define PAGE_ACCESS_TIME_BUCKETS	0
> +#endif
> +
> +#define PAGE_ACCESS_TIME_MASK				\
> +	(LAST_CPUPID_MASK << PAGE_ACCESS_TIME_BUCKETS)
> +
>  static inline int cpu_pid_to_cpupid(int cpu, int pid)
>  {
>  	return ((cpu & LAST__CPU_MASK) << LAST__PID_SHIFT) | (pid & LAST__PID_MASK);
> @@ -1346,6 +1358,11 @@ static inline bool __cpupid_match_pid(pid_t task_pid, int cpupid)
>  	return (task_pid & LAST__PID_MASK) == cpupid_to_pid(cpupid);
>  }
>  
> +static inline bool check_cpupid(int cpupid)
> +{
> +	return cpupid_to_cpu(cpupid) < nr_cpu_ids;
> +}
> +
>  #define cpupid_match_pid(task, cpupid) __cpupid_match_pid(task->pid, cpupid)
>  #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
>  static inline int page_cpupid_xchg_last(struct page *page, int cpupid)
> @@ -1374,12 +1391,25 @@ static inline void page_cpupid_reset_last(struct page *page)
>  	page->flags |= LAST_CPUPID_MASK << LAST_CPUPID_PGSHIFT;
>  }
>  #endif /* LAST_CPUPID_NOT_IN_PAGE_FLAGS */
> +
> +static inline int xchg_page_access_time(struct page *page, int time)
> +{
> +	int last_time;
> +
> +	last_time = page_cpupid_xchg_last(page, time >> PAGE_ACCESS_TIME_BUCKETS);
> +	return last_time << PAGE_ACCESS_TIME_BUCKETS;
> +}
>  #else /* !CONFIG_NUMA_BALANCING */
>  static inline int page_cpupid_xchg_last(struct page *page, int cpupid)
>  {
>  	return page_to_nid(page); /* XXX */
>  }
>  
> +static inline int xchg_page_access_time(struct page *page, int time)
> +{
> +	return 0;
> +}
> +
>  static inline int page_cpupid_last(struct page *page)
>  {
>  	return page_to_nid(page); /* XXX */
> diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
> index bb3d63bdf4ae..ad63dbfc54f1 100644
> --- a/kernel/sched/debug.c
> +++ b/kernel/sched/debug.c
> @@ -333,6 +333,7 @@ static __init int sched_init_debug(void)
>  	debugfs_create_u32("scan_period_min_ms", 0644, numa, &sysctl_numa_balancing_scan_period_min);
>  	debugfs_create_u32("scan_period_max_ms", 0644, numa, &sysctl_numa_balancing_scan_period_max);
>  	debugfs_create_u32("scan_size_mb", 0644, numa, &sysctl_numa_balancing_scan_size);
> +	debugfs_create_u32("hot_threshold_ms", 0644, numa, &sysctl_numa_balancing_hot_threshold);
>  #endif
>  
>  	debugfs_create_file("debug", 0444, debugfs_sched, NULL, &sched_debug_fops);
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index d4bd299d67ab..cb130ea46c71 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -1058,6 +1058,9 @@ unsigned int sysctl_numa_balancing_scan_size = 256;
>  /* Scan @scan_size MB every @scan_period after an initial @scan_delay in ms */
>  unsigned int sysctl_numa_balancing_scan_delay = 1000;
>  
> +/* The page with hint page fault latency < threshold in ms is considered hot */
> +unsigned int sysctl_numa_balancing_hot_threshold = 1000;
> +
>  struct numa_group {
>  	refcount_t refcount;
>  
> @@ -1400,6 +1403,37 @@ static inline unsigned long group_weight(struct task_struct *p, int nid,
>  	return 1000 * faults / total_faults;
>  }
>  
> +static bool pgdat_free_space_enough(struct pglist_data *pgdat)
> +{
> +	int z;
> +	unsigned long enough_mark;
> +
> +	enough_mark = max(1UL * 1024 * 1024 * 1024 >> PAGE_SHIFT,
> +			  pgdat->node_present_pages >> 4);
> +	for (z = pgdat->nr_zones - 1; z >= 0; z--) {
> +		struct zone *zone = pgdat->node_zones + z;
> +
> +		if (!populated_zone(zone))
> +			continue;
> +
> +		if (zone_watermark_ok(zone, 0,
> +				      high_wmark_pages(zone) + enough_mark,
> +				      ZONE_MOVABLE, 0))
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static int numa_hint_fault_latency(struct page *page)
> +{
> +	int last_time, time;
> +
> +	time = jiffies_to_msecs(jiffies);
> +	last_time = xchg_page_access_time(page, time);
> +
> +	return (time - last_time) & PAGE_ACCESS_TIME_MASK;

This code can possibly consider cold page as hot,

Assume,

LAST_CPUPID_SHIFT = 12
PAGE_ACCESS_TIME_BUCKETS = 0
sysctl_numa_balancing_hot_threshold = 1000

Assume while changing pte,
jiffies_to_msecs(jiffies) = 0xAABB0100

So value saved in page->flags will be lowest 12 bits of 0xAABB0100
which is 0x100.

Assume when numa_hint_fault_latency() gets called,
time = jiffies_to_msecs(jiffies) = 0xAACC0100

So, time = 0xAACC0100, and last_time = 0x100,
time - last_time = 0xAACC0100 - 0x100 = 0xAACC0000
0xAACC0000 & PAGE_ACCESS_TIME_MASK = 0xAACC0000 & ((1 << 12) - 1) = 0

so the return value of this function is 0, the code will consider it as
hot page but it is cold page because actual difference is
0xAACC0100 - 0xAABB0100 = 110000 ms

There may be more such scenarios. What do you think?

> +}
> +
>  bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
>  				int src_nid, int dst_cpu)
>  {
> @@ -1407,9 +1441,38 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
>  	int dst_nid = cpu_to_node(dst_cpu);
>  	int last_cpupid, this_cpupid;
>  
> +	/*
> +	 * The pages in slow memory node should be migrated according
> +	 * to hot/cold instead of accessing CPU node.
> +	 */
> +	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
> +	    !node_is_toptier(src_nid)) {
> +		struct pglist_data *pgdat;
> +		unsigned long latency, th;
> +
> +		pgdat = NODE_DATA(dst_nid);
> +		if (pgdat_free_space_enough(pgdat))
> +			return true;
> +
> +		th = sysctl_numa_balancing_hot_threshold;
> +		latency = numa_hint_fault_latency(page);
> +		if (latency >= th)
> +			return false;
> +
> +		return true;
> +	}
> +
>  	this_cpupid = cpu_pid_to_cpupid(dst_cpu, current->pid);
>  	last_cpupid = page_cpupid_xchg_last(page, this_cpupid);
>  
> +	/*
> +	 * The cpupid may be invalid when NUMA_BALANCING_MEMORY_TIERING
> +	 * is disabled dynamically.
> +	 */
> +	if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
> +	    !node_is_toptier(src_nid) && !check_cpupid(last_cpupid))
> +		return false;
> +
>  	/*
>  	 * Allow first faults or private faults to migrate immediately early in
>  	 * the lifetime of a task. The magic number 4 is based on waiting for
> @@ -2642,6 +2705,17 @@ void task_numa_fault(int last_cpupid, int mem_node, int pages, int flags)
>  	if (!p->mm)
>  		return;
>  
> +	/*
> +	 * NUMA faults statistics are unnecessary for the slow memory node.
> +	 *
> +	 * And, the cpupid may be invalid when NUMA_BALANCING_MEMORY_TIERING
> +	 * is disabled dynamically.
> +	 */
> +	if (!node_is_toptier(mem_node) &&
> +	    (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING ||
> +	     !check_cpupid(last_cpupid)))
> +		return;
> +
>  	/* Allocate buffer to track faults on a per-node basis */
>  	if (unlikely(!p->numa_faults)) {
>  		int size = sizeof(*p->numa_faults) *
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index 58263f90c559..86ce60d3d472 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -2408,6 +2408,7 @@ extern unsigned int sysctl_numa_balancing_scan_delay;
>  extern unsigned int sysctl_numa_balancing_scan_period_min;
>  extern unsigned int sysctl_numa_balancing_scan_period_max;
>  extern unsigned int sysctl_numa_balancing_scan_size;
> +extern unsigned int sysctl_numa_balancing_hot_threshold;
>  #endif
>  
>  #ifdef CONFIG_SCHED_HRTICK
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 2fe38212e07c..3a715eeeebb5 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -1401,7 +1401,7 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
>  	struct page *page;
>  	unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
>  	int page_nid = NUMA_NO_NODE;
> -	int target_nid, last_cpupid = -1;
> +	int target_nid, last_cpupid = (-1 & LAST_CPUPID_MASK);
>  	bool migrated = false;
>  	bool was_writable = pmd_savedwrite(oldpmd);
>  	int flags = 0;
> @@ -1422,7 +1422,8 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
>  		flags |= TNF_NO_GROUP;
>  
>  	page_nid = page_to_nid(page);
> -	last_cpupid = page_cpupid_last(page);
> +	if (node_is_toptier(page_nid))
> +		last_cpupid = page_cpupid_last(page);
>  	target_nid = numa_migrate_prep(page, vma, haddr, page_nid,
>  				       &flags);
>  
> @@ -1740,6 +1741,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
>  
>  	if (prot_numa) {
>  		struct page *page;
> +		bool toptier;
>  		/*
>  		 * Avoid trapping faults against the zero page. The read-only
>  		 * data is likely to be read-cached on the local CPU and
> @@ -1752,13 +1754,18 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
>  			goto unlock;
>  
>  		page = pmd_page(*pmd);
> +		toptier = node_is_toptier(page_to_nid(page));
>  		/*
>  		 * Skip scanning top tier node if normal numa
>  		 * balancing is disabled
>  		 */
>  		if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) &&
> -		    node_is_toptier(page_to_nid(page)))
> +		    toptier)
>  			goto unlock;
> +
> +		if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
> +		    !toptier)
> +			xchg_page_access_time(page, jiffies_to_msecs(jiffies));
>  	}
>  	/*
>  	 * In case prot_numa, we are under mmap_read_lock(mm). It's critical
> diff --git a/mm/memory.c b/mm/memory.c
> index b370ed118767..a8ac15ce7a75 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -74,6 +74,7 @@
>  #include <linux/perf_event.h>
>  #include <linux/ptrace.h>
>  #include <linux/vmalloc.h>
> +#include <linux/sched/sysctl.h>
>  
>  #include <trace/events/kmem.h>
>  
> @@ -4455,8 +4456,16 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
>  	if (page_mapcount(page) > 1 && (vma->vm_flags & VM_SHARED))
>  		flags |= TNF_SHARED;
>  
> -	last_cpupid = page_cpupid_last(page);
>  	page_nid = page_to_nid(page);
> +	/*
> +	 * In memory tiering mode, cpupid of slow memory page is used
> +	 * to record page access time.  So use default value.
> +	 */
> +	if ((sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
> +	    !node_is_toptier(page_nid))
> +		last_cpupid = (-1 & LAST_CPUPID_MASK);
> +	else
> +		last_cpupid = page_cpupid_last(page);
>  	target_nid = numa_migrate_prep(page, vma, vmf->address, page_nid,
>  			&flags);
>  	if (target_nid == NUMA_NO_NODE) {
> diff --git a/mm/migrate.c b/mm/migrate.c
> index dc84edfae842..e73f26dfeb38 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -534,6 +534,18 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
>  	 * future migrations of this same page.
>  	 */
>  	cpupid = page_cpupid_xchg_last(&folio->page, -1);
> +	/*
> +	 * If migrate between slow and fast memory node, reset cpupid,
> +	 * because that is used to record page access time in slow
> +	 * memory node
> +	 */
> +	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) {
> +		bool f_toptier = node_is_toptier(page_to_nid(&folio->page));
> +		bool t_toptier = node_is_toptier(page_to_nid(&newfolio->page));
> +
> +		if (f_toptier != t_toptier)
> +			cpupid = -1;
> +	}
>  	page_cpupid_xchg_last(&newfolio->page, cpupid);
>  
>  	folio_migrate_ksm(newfolio, folio);
> diff --git a/mm/mprotect.c b/mm/mprotect.c
> index b69ce7a7b2b7..e7cb90d84fac 100644
> --- a/mm/mprotect.c
> +++ b/mm/mprotect.c
> @@ -85,6 +85,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
>  			if (prot_numa) {
>  				struct page *page;
>  				int nid;
> +				bool toptier;
>  
>  				/* Avoid TLB flush if possible */
>  				if (pte_protnone(oldpte))
> @@ -114,14 +115,19 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
>  				nid = page_to_nid(page);
>  				if (target_node == nid)
>  					continue;
> +				toptier = node_is_toptier(nid);
>  
>  				/*
>  				 * Skip scanning top tier node if normal numa
>  				 * balancing is disabled
>  				 */
>  				if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) &&
> -				    node_is_toptier(nid))
> +				    toptier)
>  					continue;
> +				if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
> +				    !toptier)
> +					xchg_page_access_time(page,
> +						jiffies_to_msecs(jiffies));
>  			}
>  
>  			oldpte = ptep_modify_prot_start(vma, addr, pte);
> -- 
> 2.30.2
> 
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] memory tiering: hot page selection with hint page fault latency
  2022-04-14 13:23   ` Jagdish Gediya
@ 2022-04-15  2:42     ` ying.huang
  0 siblings, 0 replies; 8+ messages in thread
From: ying.huang @ 2022-04-15  2:42 UTC (permalink / raw)
  To: Jagdish Gediya
  Cc: Peter Zijlstra, Mel Gorman, Andrew Morton, linux-mm,
	linux-kernel, Michal Hocko, Rik van Riel, Dave Hansen, Yang Shi,
	Zi Yan, Wei Xu, osalvador, Shakeel Butt, Zhong Jiang

Hi, Jagdish,

On Thu, 2022-04-14 at 18:53 +0530, Jagdish Gediya wrote:
> On Fri, Apr 08, 2022 at 03:12:20PM +0800, Huang Ying wrote:
[snip]
> > +
> > +static int numa_hint_fault_latency(struct page *page)
> > +{
> > +	int last_time, time;
> > +
> > +	time = jiffies_to_msecs(jiffies);
> > +	last_time = xchg_page_access_time(page, time);
> > +
> > +	return (time - last_time) & PAGE_ACCESS_TIME_MASK;
> 
> This code can possibly consider cold page as hot,
> 
> Assume,
> 
> LAST_CPUPID_SHIFT = 12
> PAGE_ACCESS_TIME_BUCKETS = 0
> sysctl_numa_balancing_hot_threshold = 1000
> 
> Assume while changing pte,
> jiffies_to_msecs(jiffies) = 0xAABB0100
> 
> So value saved in page->flags will be lowest 12 bits of 0xAABB0100
> which is 0x100.
> 
> Assume when numa_hint_fault_latency() gets called,
> time = jiffies_to_msecs(jiffies) = 0xAACC0100
> 
> So, time = 0xAACC0100, and last_time = 0x100,
> time - last_time = 0xAACC0100 - 0x100 = 0xAACC0000
> 0xAACC0000 & PAGE_ACCESS_TIME_MASK = 0xAACC0000 & ((1 << 12) - 1) = 0
> 
> so the return value of this function is 0, the code will consider it as
> hot page but it is cold page because actual difference is
> 0xAACC0100 - 0xAABB0100 = 110000 ms
> 

Yes.  This is possible.

> There may be more such scenarios. What do you think?

The algorithm just works statistically correct.  That is, for really hot
pages, their hint page fault latency will be short and we can promote it
when they are accessed.  For cold pages, it's still possible for them to
be identified as hot pages.  But the possibility is much lower than that
of the hot pages.

We can try to improve further here.  But as the first step, I want to
keep the algorithm as simple as possible.  Then we can try improve it
step by step and show benefit in each step to justify the further
optimization.

> > +}
> > +

Best Regards,
Huang, Ying




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-04-15  2:42 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-08  7:12 [PATCH 0/3] memory tiering: hot page selection Huang Ying
2022-04-08  7:12 ` [PATCH 1/3] memory tiering: hot page selection with hint page fault latency Huang Ying
2022-04-14 13:23   ` Jagdish Gediya
2022-04-15  2:42     ` ying.huang
2022-04-08  7:12 ` [PATCH 2/3] memory tiering: rate limit NUMA migration throughput Huang Ying
2022-04-08  7:12 ` [PATCH 3/3] memory tiering: adjust hot threshold automatically Huang Ying
2022-04-09  4:07 ` [PATCH 0/3] memory tiering: hot page selection Andrew Morton
2022-04-11  8:16   ` ying.huang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).