Re: [PATCH v6 1/2] mm/vmscan: Skip memcg with !usage in shrink_node_memcgs()

From: Waiman Long
Date: Mon Apr 14 2025 - 09:16:47 EST



On 4/14/25 8:42 AM, Michal Koutný wrote:
On Sun, Apr 13, 2025 at 10:12:48PM -0400, Waiman Long <longman@xxxxxxxxxx> wrote:
2) memory.low is set to a non-zero value but the cgroup has no task in
it so that it has an effective low value of 0. Again it may have a
non-zero low event count if memory reclaim happens. This is probably
not a result expected by the users and it is really doubtful that
users will check an empty cgroup with no task in it and expecting
some non-zero event counts.
I think you want to distinguish "no tasks" vs "no usage" in this
paragraph.

Good point. Will update it if I need to send a new version.


--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5963,6 +5963,10 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)
mem_cgroup_calculate_protection(target_memcg, memcg);
+ /* Skip memcg with no usage */
+ if (!mem_cgroup_usage(memcg, false))
+ continue;
+
if (mem_cgroup_below_min(target_memcg, memcg)) {
As I think more about this -- the idea expressed by the diff makes
sense. But is it really a change?
For non-root memcgs, they'll be skipped because 0 >= 0 (in
mem_cgroup_below_min()) and root memcg would hardly be skipped.

I did see some low event in the no usage case because of the ">=" comparison used in mem_cgroup_below_min(). I originally planning to guard against the elow == 0 case but Johannes advised against it.




--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -380,10 +380,10 @@ static bool reclaim_until(const char *memcg, long goal);
*
* Then it checks actual memory usages and expects that:
* A/B memory.current ~= 50M
- * A/B/C memory.current ~= 29M
- * A/B/D memory.current ~= 21M
- * A/B/E memory.current ~= 0
- * A/B/F memory.current = 0
+ * A/B/C memory.current ~= 29M [memory.events:low > 0]
+ * A/B/D memory.current ~= 21M [memory.events:low > 0]
+ * A/B/E memory.current ~= 0 [memory.events:low == 0 if !memory_recursiveprot, > 0 otherwise]
Please note the subtlety in my suggestion -- I want the test with
memory_recursiveprot _not_ to check events count at all. Because:
a) it forces single interpretation of low events wrt effective
low limit
b) effective low limit should still be 0 in E in this testcase
(there should be no unclaimed protection of C and D).

Yes, low event count for E is 0 in the !memory_recursiveprot case, but C/D still have low events and setting no_low_events_index to -1 will fail the test and it is not the same as not checking low event counts at all.

Cheers,
Longman