Skip to contentSkip to conversation

mm/damon: fix DAMOS bugs in core, paddr and vaddr

PATCH Applied SJ Park 2 participants 20 messages Sep 1

Subsystems (3)
lore
Share & tools
lore

SJ Park

author maintainer
#0 lore raw

cover mm/damon: fix DAMOS bugs in core, paddr and vaddr

Fix misc bugs of DAMOS. Patch 1 makes DAMOS less stress memory allocator under extreme situation. Patches 2 and 3 fix wrong folios walking in DAMON_PADDR. Patches 4 and 5 fix wrong folios walking in DAMON_VADDR. Patches 6-8 handle extreme and unlikely memory situations that can cause divide by zero and underflow.

All bugs are discovered by Sashiko.

The bugs are not very critical, but better to be merged sooner than later. Since mm.git is closed for urgent changes, I aim for 7.3-rcX. I will drop the RFC tag after 7.3-rc1.

Changes from RFC v1.2
- RFC v1.2: https://lore.kernel.org/[email protected]
- Drop RFC tag.
- Rebase to latest mm-new.
Changes from RFC v1.1
- RFC v1.1: https://lore.kernel.org/[email protected]
- Fix wrong commit message (patch 7).
- Return 100% and 0% for used and free memory (patches 7 and 8).
- Drop incomplete last_psi_total initialization (patch 2).
- Rebase to latest mm-new.
Changes from RFC
- RFC: https://lore.kernel.org/[email protected]
- Use cached pte content (patch 5 and 6).
- Handle totalram < freeram case (patch 7).
- Fix wrong function name in commit message (patch 9).

SJ Park (8):
mm/damon/core: skip applying scheme if region split for quota fails mm/damon/paddr: respect folio end for DAMOS_STAT mm/damon/paddr: respect folio end for DAMOS actions except STAT mm/damon/vaddr: respect folio end for DAMOS_STAT mm/damon/vaddr: respect folio end for DAMOS_MIGRATE_{HOT,COLD} mm/damon/core: handle extreme memory state in damon_get_node_mem_bp() mm/damon/core: handle extreme memory state in get_node_memcg_used_bp() mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

 mm/damon/core.c  | 18 ++++++++++++++++--
 mm/damon/paddr.c |  8 ++++----
 mm/damon/vaddr.c | 10 ++++++++--
 3 files changed, 28 insertions(+), 8 deletions(-)

base-commit: 0926ef526f90a707f59c0790cafdb5a9b9cdebe7

Signature · 1 line
2.47.3
author maintainer
#1 lore raw

PATCH 1/8 mm/damon/core: skip applying scheme if region split for quota fails

damos_apply_scheme() splits a region and apply the action to the subregion if it is needed for not violating the quota. The split operation (damon_split_region_at()) could fail for allocation failure. In the case, the quota could be violated. From the user's perspective, DAMOS becomes more aggressive than expected under the extreme situation. Handle the failure.

The user impact is not critical. The failure of damon_split_region_at() is unlikely since it is arguably too small to fail. Also DAMOS being aggressive is limited to the single region. Users can set min_nr_regions to set the maximum size of each region. If it is reasonably set, the transient overhead shouldn't be critical.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 2b8a248d5873 ("mm/damon/schemes: implement size quota for schemes application speed control")
Cc <[email protected]> # 5.16.x
---
 mm/damon/core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
mm/damon/core.c +2 -1
index 3f89cfdf5f022..a6fdb3068262d 100644
@@ -2604,7 +2604,8 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
 					c->min_region_sz);
 			if (!sz)
 				goto update_stat;
-			damon_split_region_at(t, r, sz);
+			if (damon_split_region_at(t, r, sz))
+				goto update_stat;
 		}
 		if (damos_core_filter_out(c, t, r, s))
 			return;
Signature · 1 line
2.47.3
#2 lore raw

Re: [PATCH 1/8] mm/damon/core: skip applying scheme if region split for quota fails

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 13 lines

damos_apply_scheme() splits a region and apply the action to the subregion if it is needed for not violating the quota. The split operation (damon_split_region_at()) could fail for allocation failure. In the case, the quota could be violated. From the user's perspective, DAMOS becomes more aggressive than expected under the extreme situation. Handle the failure.

The user impact is not critical. The failure of damon_split_region_at() is unlikely since it is arguably too small to fail. Also DAMOS being aggressive is limited to the single region. Users can set min_nr_regions to set the maximum size of each region. If it is reasonably set, the transient overhead shouldn't be critical.

Quoted 7 lines

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 2b8a248d5873 ("mm/damon/schemes: implement size quota for schemes application speed control")
Cc <[email protected]> # 5.16.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
author maintainer
#3 lore raw

PATCH 2/8 mm/damon/paddr: respect folio end for DAMOS_STAT

In reply to SJ Park

The function for applying DAMOS_STAT in DAMON physical address space operation set (paddr), namely damon_pa_stat(), applies DAMOS filters to folios of the given region. For that, it gets folios of addresses in the region. It starts from the region start address and advances the address by the size of the folio of the address until it goes out of the region. If the start address is in the middle of a large folio, and if the next folios are small, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that the DAMOS_STAT-based page level monitoring results become inaccurate. Since the page level monitoring is supposed to provide relatively high precision, this is definitely a problem. It is arguably not critical since it is only monitoring quality degradation.

Fixes bdbe1d7bc325 ("mm/damon/paddr: increment pa_stat damon address range by folio size")
Cc <[email protected]> # 6.14.x
---
 mm/damon/paddr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
mm/damon/paddr.c +1 -1
index 5c6c3a597fd0b..2ab7b3842701e 100644
@@ -379,7 +379,7 @@ static unsigned long damon_pa_stat(struct damon_region *r,
 
 		if (!damos_pa_filter_out(s, folio))
 			*sz_filter_passed += folio_size(folio) / addr_unit;
-		addr += folio_size(folio);
+		addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
 		folio_put(folio);
 	}
 	s->last_applied = folio;
Signature · 1 line
2.47.3
#4 lore raw

Re: [PATCH 2/8] mm/damon/paddr: respect folio end for DAMOS_STAT

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 16 lines

The function for applying DAMOS_STAT in DAMON physical address space operation set (paddr), namely damon_pa_stat(), applies DAMOS filters to folios of the given region. For that, it gets folios of addresses in the region. It starts from the region start address and advances the address by the size of the folio of the address until it goes out of the region. If the start address is in the middle of a large folio, and if the next folios are small, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that the DAMOS_STAT-based page level monitoring results become inaccurate. Since the page level monitoring is supposed to provide relatively high precision, this is definitely a problem. It is arguably not critical since it is only monitoring quality degradation.

Quoted 3 lines
Fixes bdbe1d7bc325 ("mm/damon/paddr: increment pa_stat damon address range by folio size")
Cc <[email protected]> # 6.14.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author maintainer
#5 lore raw

PATCH 3/8 mm/damon/paddr: respect folio end for DAMOS actions except STAT

In reply to SJ Park

A few functions for applying DAMOS actions including pageout, lru_[de]prio and migrate_{hot,cold} in DAMON physical address space operation set (paddr) collect folios of the given region by getting the folios of region-internal addresses. Then, those functions apply the action to the collected folios at once. The collection starts from the region start address and advances the address by the size of the folio of the address until it goes out of the region. If the start address is in the middle of a large folio, and if the next folios are small, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that DAMOS action is applied to less than expected amount of memory. Given the best effort nature of DAMON, it is no big problem, but it is clearly a bug that is better to be fixed.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 3a06696305e7 ("mm/damon/ops: have damon_get_folio return folio even for tail pages")
Cc <[email protected]> # 6.15.x
---
 mm/damon/paddr.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
mm/damon/paddr.c +3 -3
index 2ab7b3842701e..9ddd1ec8202b7 100644
@@ -264,7 +264,7 @@ static unsigned long damon_pa_pageout(struct damon_region *r,
 		else
 			list_add(&folio->lru, &folio_list);
 put_folio:
-		addr += folio_size(folio);
+		addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
 		folio_put(folio);
 	}
 	if (install_young_filter)
@@ -302,7 +302,7 @@ static inline unsigned long damon_pa_de_activate(
 			folio_deactivate(folio);
 		applied += folio_nr_pages(folio);
 put_folio:
-		addr += folio_size(folio);
+		addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
 		folio_put(folio);
 	}
 	s->last_applied = folio;
@@ -350,7 +350,7 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
 				folio_is_file_lru(folio));
 		list_add(&folio->lru, &folio_list);
 put_folio:
-		addr += folio_size(folio);
+		addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
 		folio_put(folio);
 	}
 	applied = damon_migrate_pages(&folio_list, s->target_nid);
Signature · 1 line
2.47.3
#6 lore raw

Re: [PATCH 3/8] mm/damon/paddr: respect folio end for DAMOS actions except STAT

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines

A few functions for applying DAMOS actions including pageout, lru_[de]prio and migrate_{hot,cold} in DAMON physical address space operation set (paddr) collect folios of the given region by getting the folios of region-internal addresses. Then, those functions apply the action to the collected folios at once. The collection starts from the region start address and advances the address by the size of the folio of the address until it goes out of the region. If the start address is in the middle of a large folio, and if the next folios are small, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that DAMOS action is applied to less than expected amount of memory. Given the best effort nature of DAMON, it is no big problem, but it is clearly a bug that is better to be fixed.

Quoted 7 lines

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 3a06696305e7 ("mm/damon/ops: have damon_get_folio return folio even for tail pages")
Cc <[email protected]> # 6.15.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author maintainer
#7 lore raw

PATCH 4/8 mm/damon/vaddr: respect folio end for DAMOS_STAT

In reply to SJ Park

For applying DAMOS_STAT action to a region, DAMON virtual address space operation set (vaddr) calls walk_page_range[_vma]() for the region. The pmd walk entry function, namely damon_va_stat_pmd_entry(), applies DAMOS filters to folios of addresses of the region in the pmd. It starts from the walking address and advances the address by the size of the folio of the address until it goes out of the pmd or the region.

Let's suppose it is for the first pmd of the region, and the region start address is in the middle of a large folio. Also, the next folios are small. Then, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that the DAMOS_STAT-based page level monitoring results become inaccurate. Since the page level monitoring is supposed to provide relatively high precision, this is definitely a problem. It is arguably not critical since it is only monitoring quality degradation.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 63f39737d1e3 ("mm/damon/vaddr: support stat-purpose DAMOS filters")
Cc <[email protected]> # 6.18.x
---
 mm/damon/vaddr.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
mm/damon/vaddr.c +4 -1
index 0648400b2d65b..4b0b5edf67952 100644
@@ -831,6 +831,8 @@ static int damos_va_stat_pmd_entry(pmd_t *pmd, unsigned long addr,
 		return 0;
 
 	for (; addr < next; pte += nr, addr += nr * PAGE_SIZE) {
+		unsigned long page_idx;
+
 		nr = 1;
 		ptent = ptep_get(pte);
 
@@ -844,7 +846,8 @@ static int damos_va_stat_pmd_entry(pmd_t *pmd, unsigned long addr,
 
 		if (!damos_va_filter_out(s, folio, vma, addr, pte, NULL))
 			*sz_filter_passed += folio_size(folio);
-		nr = folio_nr_pages(folio);
+		page_idx = folio_page_idx(folio, pte_page(ptent));
+		nr = folio_nr_pages(folio) - page_idx;
 		s->last_applied = folio;
 	}
 	pte_unmap_unlock(start_pte, ptl);
Signature · 1 line
2.47.3
#8 lore raw

Re: [PATCH 4/8] mm/damon/vaddr: respect folio end for DAMOS_STAT

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 19 lines

For applying DAMOS_STAT action to a region, DAMON virtual address space operation set (vaddr) calls walk_page_range[_vma]() for the region. The pmd walk entry function, namely damon_va_stat_pmd_entry(), applies DAMOS filters to folios of addresses of the region in the pmd. It starts from the walking address and advances the address by the size of the folio of the address until it goes out of the pmd or the region.

Let's suppose it is for the first pmd of the region, and the region start address is in the middle of a large folio. Also, the next folios are small. Then, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that the DAMOS_STAT-based page level monitoring results become inaccurate. Since the page level monitoring is supposed to provide relatively high precision, this is definitely a problem. It is arguably not critical since it is only monitoring quality degradation.

Quoted 7 lines

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 63f39737d1e3 ("mm/damon/vaddr: support stat-purpose DAMOS filters")
Cc <[email protected]> # 6.18.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author maintainer
#9 lore raw

PATCH 5/8 mm/damon/vaddr: respect folio end for DAMOS_MIGRATE_{HOT,COLD}

In reply to SJ Park

For applying DAMOS_MIGRATE_{HOT,COLD} actions to a region, DAMON virtual address space operation set (vaddr) calls walk_page_range[_vma]() for the region. The pmd walk entry function, namely damon_va_migrate_pmd_entry(), collects folios of addresses of the region in the pmd. It starts from the walking address and advances the address by the size of the folio of the address until it goes out of the pmd or the region.

Let's suppose it is for the first pmd of the region, and the region start address is in the middle of a large folio. Also, the next folios are small. Then, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that DAMOS action is applied to less than expected amount of memory. Given the best effort nature of DAMON, it is no big problem, but it is clearly a bug that is better to be fixed.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 09efc56a3b1c ("mm/damon/vaddr: consistently use only pmd_entry for damos_migrate")
Cc <[email protected]> # 6.19.x
---
 mm/damon/vaddr.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
mm/damon/vaddr.c +4 -1
index 4b0b5edf67952..c8c32b2ae0402 100644
@@ -669,6 +669,8 @@ static int damos_va_migrate_pmd_entry(pmd_t *pmd, unsigned long addr,
 		return 0;
 
 	for (; addr < next; pte += nr, addr += nr * PAGE_SIZE) {
+		unsigned long page_idx;
+
 		nr = 1;
 		ptent = ptep_get(pte);
 
@@ -681,7 +683,8 @@ static int damos_va_migrate_pmd_entry(pmd_t *pmd, unsigned long addr,
 			continue;
 		damos_va_migrate_dests_add(folio, walk->vma, addr, dests,
 				migration_lists);
-		nr = folio_nr_pages(folio);
+		page_idx = folio_page_idx(folio, pte_page(ptent));
+		nr = folio_nr_pages(folio) - page_idx;
 	}
 	pte_unmap_unlock(start_pte, ptl);
 	return 0;
Signature · 1 line
2.47.3
#10 lore raw

Re: [PATCH 5/8] mm/damon/vaddr: respect folio end for DAMOS_MIGRATE_{HOT,COLD}

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines

For applying DAMOS_MIGRATE_{HOT,COLD} actions to a region, DAMON virtual address space operation set (vaddr) calls walk_page_range[_vma]() for the region. The pmd walk entry function, namely damon_va_migrate_pmd_entry(), collects folios of addresses of the region in the pmd. It starts from the walking address and advances the address by the size of the folio of the address until it goes out of the pmd or the region.

Let's suppose it is for the first pmd of the region, and the region start address is in the middle of a large folio. Also, the next folios are small. Then, some of the next folios could be skipped. Fix the issue by advancing the address to exactly the start address of the next folio.

The user impact is that DAMOS action is applied to less than expected amount of memory. Given the best effort nature of DAMON, it is no big problem, but it is clearly a bug that is better to be fixed.

Quoted 7 lines

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 09efc56a3b1c ("mm/damon/vaddr: consistently use only pmd_entry for damos_migrate")
Cc <[email protected]> # 6.19.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
author maintainer
#11 lore raw

PATCH 6/8 mm/damon/core: handle extreme memory state in damon_get_node_mem_bp()

In reply to SJ Park

In an extreme and unlikely situation, si_meminfo_node() might let the caller show zero total ram. That could cause a divide by zero in damon_get_node_mem_bp(). It could also show free memory larger than the total memory. This could cause underflow and make DAMOS temporarily make unexpected behavior. Thanks to safety guards in the auto-tuning feedback loop, that should not be a real problem, though. Fix the problems by respectively returning 100% and 0% for used and free memory queries in the corner cases.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 0e1c773b501f ("mm/damon/core: introduce damos quota goal metrics for memory node utilization")
Cc <[email protected]> # 6.16.x
---
 mm/damon/core.c | 7 +++++++
 1 file changed, 7 insertions(+)
mm/damon/core.c +7 -0
index a6fdb3068262d..ad3657d356fbc 100644
@@ -2807,6 +2807,13 @@ static __kernel_ulong_t damos_get_node_mem_bp(
 	}
 
 	si_meminfo_node(&i, goal->nid);
+	if (!i.totalram || i.totalram < i.freeram) {
+		if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
+			return 10000;
+		else	/* DAMOS_QUOTA_NODE_MEM_FREE_BP */
+			return 0;
+	}
+
 	if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
 		numerator = i.totalram - i.freeram;
 	else	/* DAMOS_QUOTA_NODE_MEM_FREE_BP */
Signature · 1 line
2.47.3
#12 lore raw

Re: [PATCH 6/8] mm/damon/core: handle extreme memory state in damon_get_node_mem_bp()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines

In an extreme and unlikely situation, si_meminfo_node() might let the caller show zero total ram. That could cause a divide by zero in damon_get_node_mem_bp(). It could also show free memory larger than the total memory. This could cause underflow and make DAMOS temporarily make unexpected behavior. Thanks to safety guards in the auto-tuning feedback loop, that should not be a real problem, though. Fix the problems by respectively returning 100% and 0% for used and free memory queries in the corner cases.

Quoted 7 lines

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 0e1c773b501f ("mm/damon/core: introduce damos quota goal metrics for memory node utilization")
Cc <[email protected]> # 6.16.x

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
author maintainer
#13 lore raw

PATCH 7/8 mm/damon/core: handle extreme memory state in get_node_memcg_used_bp()

In reply to SJ Park

In extreme unlikely situations, total memory might be zero. In less extreme but still very unlikely situations, lruvec_page_state() calls might let the caller show used memory larger than total memory. In the two cases, damos_get_node_memcg_used_bp() could cause division by zero, or return underflowed value, respectively. Handle the cases by respectively returning 100% and 0% for used and free memory queries in the corner cases.

This issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes b74a120bcf50 ("mm/damon/core: implement DAMOS_QUOTA_NODE_MEMCG_USED_BP")
Cc <[email protected]> # 6.19.x
---
 mm/damon/core.c | 6 ++++++
 1 file changed, 6 insertions(+)
mm/damon/core.c +6 -0
index ad3657d356fbc..3060edf5e4fa6 100644
@@ -2854,6 +2854,12 @@ static unsigned long damos_get_node_memcg_used_bp(
 	mem_cgroup_put(memcg);
 
 	si_meminfo_node(&i, goal->nid);
+	if (!i.totalram || i.totalram < used_pages) {
+		if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
+			return 10000;
+		else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */
+			return 0;
+	}
 	if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
 		numerator = used_pages;
 	else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */
Signature · 1 line
2.47.3
#14 lore raw

Re: [PATCH 7/8] mm/damon/core: handle extreme memory state in get_node_memcg_used_bp()

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Integer overflow in basis point calculation on 32-bit systems for large memory sizes.
- [High] Incorrect saturation logic for negative global memory utilization triggers false maximum reclaim. --

commit 23b94672ac5d1c09d669ade5732261e82d129c96
Author: SJ Park <[email protected]>

mm/damon/core: handle extreme memory state in get_node_memcg_used_bp()

This commit handles corner cases where total memory is zero or lruvec_page_state returns more used memory than total memory. It returns 100% or 0% for used and free memory queries respectively in these scenarios.

Quoted 5 lines
mm/damon/core.c +0 -0
index ad3657d356fbc..3060edf5e4fa6 100644

mm/damon/core.c static unsigned long damos_get_node_memcg_used_bp( · line 2862

Quoted 9 lines
mm/damon/core.c +6 -0
@@ -2854,6 +2854,12 @@ static unsigned long damos_get_node_memcg_used_bp(
 	mem_cgroup_put(memcg);
 
 	si_meminfo_node(&i, goal->nid);
+	if (!i.totalram || i.totalram < used_pages) {
+		if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
+			return 10000;
+		else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */
+			return 0;
+	}

[Severity: High]
This isn't a bug introduced by this patch, but looking at similar logic in damos_get_node_mem_bp():

    if (!i.totalram || i.totalram < i.freeram) {
        if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
            return 10000;
        else    /* DAMOS_QUOTA_NODE_MEM_FREE_BP */
            return 0;
    }

Can this regression trigger unwarranted memory reclaim pressure? If transient per-CPU stats drift causes i.freeram to slightly exceed i.totalram, this condition evaluates to true.

If free memory exceeds total memory, it implies used memory is less than 0. Should the correct saturation be 0% used and 100% free instead? Returning 10000 (100%) for used memory tells DAMON that the empty node is completely full, which might cause aggressive reclaim behavior on mostly empty nodes.

mm/damon/core.c static unsigned long damos_get_node_memcg_used_bp( · line 2865

Quoted 3 lines
mm/damon/core.c +0 -0
 	if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
 		numerator = used_pages;
 	else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */

[ ... ]

[Severity: High]
This is a pre-existing issue, but does this basis point calculation overflow on 32-bit systems for large memory sizes?

A little further down in damos_get_node_memcg_used_bp() (and similarly in damos_get_node_mem_bp()):

    return mult_frac(numerator, 10000, i.totalram);

The mult_frac macro evaluates its internal remainder multiplication using the type of the numerator. On 32-bit systems, the numerator is a 32-bit unsigned long.

If the numerator is greater than 429,496 (which corresponds to 1.75 GB of RAM with 4K pages), the multiplication by 10000 wraps around ULONG_MAX. For instance, 429,497 * 10000 modulo 2^32 evaluates to 2704, which then divided by total RAM evaluates to near 0%.

Could this regression lead to incorrect memory quota enforcement for memcgs or nodes exceeding this threshold?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
author maintainer
#15 lore raw

Re: [PATCH 7/8] mm/damon/core: handle extreme memory state in get_node_memcg_used_bp()

Quoted 44 lines · On Tue, 01 Sep 2026 13:41:08 +0000 [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Integer overflow in basis point calculation on 32-bit systems for large memory sizes.
- [High] Incorrect saturation logic for negative global memory utilization triggers false maximum reclaim. --

commit 23b94672ac5d1c09d669ade5732261e82d129c96
Author: SJ Park <[email protected]>

mm/damon/core: handle extreme memory state in get_node_memcg_used_bp()

This commit handles corner cases where total memory is zero or lruvec_page_state returns more used memory than total memory. It returns 100% or 0% for used and free memory queries respectively in these scenarios.

Quoted 5 lines
mm/damon/core.c +0 -0
index ad3657d356fbc..3060edf5e4fa6 100644

mm/damon/core.c static unsigned long damos_get_node_memcg_used_bp( · line 2862

Quoted 9 lines
mm/damon/core.c +6 -0
@@ -2854,6 +2854,12 @@ static unsigned long damos_get_node_memcg_used_bp(
 	mem_cgroup_put(memcg);
 
 	si_meminfo_node(&i, goal->nid);
+	if (!i.totalram || i.totalram < used_pages) {
+		if (goal->metric = DAMOS_QUOTA_NODE_MEMCG_USED_BP)
+			return 10000;
+		else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */
+			return 0;
+	}

[Severity: High]
This isn't a bug introduced by this patch, but looking at similar logic in damos_get_node_mem_bp():

    if (!i.totalram || i.totalram < i.freeram) {
        if (goal->metric = DAMOS_QUOTA_NODE_MEM_USED_BP)
            return 10000;
        else    /* DAMOS_QUOTA_NODE_MEM_FREE_BP */
            return 0;
    }
Quoted 8 lines

Can this regression trigger unwarranted memory reclaim pressure? If transient per-CPU stats drift causes i.freeram to slightly exceed i.totalram, this condition evaluates to true.

If free memory exceeds total memory, it implies used memory is less than 0. Should the correct saturation be 0% used and 100% free instead? Returning 10000 (100%) for used memory tells DAMON that the empty node is completely full, which might cause aggressive reclaim behavior on mostly empty nodes.

No issue. It is a different perspective. And DAMOS auto-tuning logic limits increasing quota arbitrarily. Increasing quota is capped to only 2x of the current effective quota.

Quoted 20 lines

mm/damon/core.c static unsigned long damos_get_node_memcg_used_bp( · line 2865

Quoted 4 lines
mm/damon/core.c +0 -0
 	if (goal->metric = DAMOS_QUOTA_NODE_MEMCG_USED_BP)
 		numerator = used_pages;
 	else	/* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */

[ ... ]

[Severity: High]
This is a pre-existing issue, but does this basis point calculation overflow on 32-bit systems for large memory sizes?

A little further down in damos_get_node_memcg_used_bp() (and similarly in damos_get_node_mem_bp()):

    return mult_frac(numerator, 10000, i.totalram);

The mult_frac macro evaluates its internal remainder multiplication using the type of the numerator. On 32-bit systems, the numerator is a 32-bit unsigned long.

Quoted 7 lines

If the numerator is greater than 429,496 (which corresponds to 1.75 GB of RAM with 4K pages), the multiplication by 10000 wraps around ULONG_MAX. For instance, 429,497 * 10000 modulo 2^32 evaluates to 2704, which then divided by total RAM evaluates to near 0%.

Could this regression lead to incorrect memory quota enforcement for memcgs or nodes exceeding this threshold?

Known low priority issue.

Quoted 4 lines
Signature · 2 lines
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7

Thanks,
SJ

author maintainer
#16 lore raw

PATCH 8/8 mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

In reply to SJ Park

damos_get_in_active_mem_bp() uses the sum of the active and inactive memory amount as a denominator. In an extreme and unlikely environment, active and inactive memory might be zero. In this case, hence, it results in a divide by zero problem. Avoid it by changing the denominator to one if it is zero, before it is being used.

The issue was discovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 4835e2871321 ("mm/damon/core: introduce [in]active memory ratio damos quota goal metric")
Cc <[email protected]> # 7.0.x
---
 mm/damon/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
mm/damon/core.c +1 -1
index 3060edf5e4fa6..0df785e72438f 100644
@@ -3006,7 +3006,7 @@ static unsigned int damos_get_in_active_mem_bp(bool active_ratio)
 		global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
 	inactive = global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON) +
 		global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
-	total = active + inactive;
+	total = max(active + inactive, 1);
 	if (active_ratio)
 		return mult_frac(active, 10000, total);
 	return mult_frac(inactive, 10000, total);
Signature · 1 line
2.47.3
#17 lore raw

Re: [PATCH 8/8] mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] 32-bit integer overflow in DAMOS metric calculations. --

commit 4a4658bd5d39473e6ccb07618a88e601987f2acf
Author: SJ Park <[email protected]>

mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

The function damos_get_in_active_mem_bp() uses the sum of active and inactive memory as a denominator. In extreme environments where both are zero, this results in a divide by zero error. This patch avoids the issue by changing the denominator to one if it is zero.

mm/damon/core.c static unsigned int damos_get_in_active_mem_bp(bool active_ratio) · line 3012

Quoted 13 lines
mm/damon/core.c +1 -1
index 3060edf5e4fa6..0df785e72438f 100644
@@ -3006,7 +3006,7 @@ static unsigned int damos_get_in_active_mem_bp(bool active_ratio)
 		global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
 	inactive = global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON) +
 		global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
-	total = active + inactive;
+	total = max(active + inactive, 1);
 	if (active_ratio)
 		return mult_frac(active, 10000, total);
 	return mult_frac(inactive, 10000, total);

[Severity: High]
This isn't a bug introduced by this patch, but it's a pre-existing high severity issue in the exact function modified.

Could this mult_frac() calculation overflow on 32-bit architectures?

When active or inactive memory exceeds approximately 429,496 pages (about 1.7GB) on a 32-bit system, the intermediate product in mult_frac():
(r * n / d) where r is (active % total) and n is 10000

will overflow the 32-bit unsigned long before division. This wraps around and causes Damon to apply completely wrong quotas, degrading memory goal reliability.

Does this same 32-bit overflow issue also affect other metric calculations in mm/damon/core.c, such as damos_get_node_mem_bp() and damos_get_node_memcg_used_bp() which similarly compute:
mult_frac(numerator, 10000, i.totalram) ?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
author maintainer
#18 lore raw

Re: [PATCH 8/8] mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

Quoted 42 lines · On Tue, 01 Sep 2026 13:36:50 +0000 [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] 32-bit integer overflow in DAMOS metric calculations. --

commit 4a4658bd5d39473e6ccb07618a88e601987f2acf
Author: SJ Park <[email protected]>

mm/damon/core: handle extreme memory state in get_in_active_mem_bp()

The function damos_get_in_active_mem_bp() uses the sum of active and inactive memory as a denominator. In extreme environments where both are zero, this results in a divide by zero error. This patch avoids the issue by changing the denominator to one if it is zero.

mm/damon/core.c static unsigned int damos_get_in_active_mem_bp(bool active_ratio) · line 3012

Quoted 13 lines
mm/damon/core.c +1 -1
index 3060edf5e4fa6..0df785e72438f 100644
@@ -3006,7 +3006,7 @@ static unsigned int damos_get_in_active_mem_bp(bool active_ratio)
 		global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
 	inactive = global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON) +
 		global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
-	total = active + inactive;
+	total = max(active + inactive, 1);
 	if (active_ratio)
 		return mult_frac(active, 10000, total);
 	return mult_frac(inactive, 10000, total);

[Severity: High]
This isn't a bug introduced by this patch, but it's a pre-existing high severity issue in the exact function modified.

Could this mult_frac() calculation overflow on 32-bit architectures?

When active or inactive memory exceeds approximately 429,496 pages (about 1.7GB) on a 32-bit system, the intermediate product in mult_frac():
(r * n / d) where r is (active % total) and n is 10000

Quoted 8 lines

will overflow the 32-bit unsigned long before division. This wraps around and causes Damon to apply completely wrong quotas, degrading memory goal reliability.

Does this same 32-bit overflow issue also affect other metric calculations in mm/damon/core.c, such as damos_get_node_mem_bp() and damos_get_node_memcg_used_bp() which similarly compute:
mult_frac(numerator, 10000, i.totalram) ?

Known low priority issue.

Quoted 4 lines
Signature · 2 lines
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8

Thanks,
SJ

author maintainer
#19 lore raw
In reply to SJ Park
Quoted 5 lines · On Tue, 1 Sep 2026 06:18:41 -0700 SJ Park <[email protected]> wrote:

Fix misc bugs of DAMOS. Patch 1 makes DAMOS less stress memory allocator under extreme situation. Patches 2 and 3 fix wrong folios walking in DAMON_PADDR. Patches 4 and 5 fix wrong folios walking in DAMON_VADDR. Patches 6-8 handle extreme and unlikely memory situations that can cause divide by zero and underflow.

Sashiko found no blocker for this series. Sashiko sent findings to damon@ mailing list [1], and I replied to all the comments. Please read those for details.

[1] https://lore.kernel.org/damon/

Thanks,
SJ

[...]