]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mm/memcg: make memory.reclaim interface generic
authorDavidlohr Bueso <dave@stgolabs.net>
Mon, 23 Jun 2025 18:58:49 +0000 (11:58 -0700)
committerAndrew Morton <akpm@linux-foundation.org>
Sun, 20 Jul 2025 01:59:52 +0000 (18:59 -0700)
This adds a general call for both parsing as well as the common reclaim
semantics.  memcg is still the only user and no change in semantics.

[akpm@linux-foundation.org: fix CONFIG_NUMA=n build]
Link: https://lkml.kernel.org/r/20250623185851.830632-3-dave@stgolabs.net
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/internal.h
mm/memcontrol.c
mm/vmscan.c

index 91773a0ef305615a2e48642891a12ecb9b9d080f..5b0f71e5434b2a43bf50220b1e3ef72ccf69d93b 100644 (file)
@@ -533,6 +533,16 @@ extern unsigned long highest_memmap_pfn;
 bool folio_isolate_lru(struct folio *folio);
 void folio_putback_lru(struct folio *folio);
 extern void reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason);
+#ifdef CONFIG_NUMA
+int user_proactive_reclaim(char *buf,
+                          struct mem_cgroup *memcg, pg_data_t *pgdat);
+#else
+static inline int user_proactive_reclaim(char *buf,
+                          struct mem_cgroup *memcg, pg_data_t *pgdat)
+{
+       return 0;
+}
+#endif
 
 /*
  * in mm/rmap.c:
index 70fdeda1120b3ef3ac40504dfcc04271342ec12f..235c66d2161b2633135e42a45315723dd7511ca0 100644 (file)
@@ -51,7 +51,6 @@
 #include <linux/spinlock.h>
 #include <linux/fs.h>
 #include <linux/seq_file.h>
-#include <linux/parser.h>
 #include <linux/vmpressure.h>
 #include <linux/memremap.h>
 #include <linux/mm_inline.h>
@@ -4564,83 +4563,15 @@ static ssize_t memory_oom_group_write(struct kernfs_open_file *of,
        return nbytes;
 }
 
-enum {
-       MEMORY_RECLAIM_SWAPPINESS = 0,
-       MEMORY_RECLAIM_SWAPPINESS_MAX,
-       MEMORY_RECLAIM_NULL,
-};
-
-static const match_table_t tokens = {
-       { MEMORY_RECLAIM_SWAPPINESS, "swappiness=%d"},
-       { MEMORY_RECLAIM_SWAPPINESS_MAX, "swappiness=max"},
-       { MEMORY_RECLAIM_NULL, NULL },
-};
-
 static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
                              size_t nbytes, loff_t off)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
-       unsigned int nr_retries = MAX_RECLAIM_RETRIES;
-       unsigned long nr_to_reclaim, nr_reclaimed = 0;
-       int swappiness = -1;
-       unsigned int reclaim_options;
-       char *old_buf, *start;
-       substring_t args[MAX_OPT_ARGS];
-
-       buf = strstrip(buf);
-
-       old_buf = buf;
-       nr_to_reclaim = memparse(buf, &buf) / PAGE_SIZE;
-       if (buf == old_buf)
-               return -EINVAL;
-
-       buf = strstrip(buf);
-
-       while ((start = strsep(&buf, " ")) != NULL) {
-               if (!strlen(start))
-                       continue;
-               switch (match_token(start, tokens, args)) {
-               case MEMORY_RECLAIM_SWAPPINESS:
-                       if (match_int(&args[0], &swappiness))
-                               return -EINVAL;
-                       if (swappiness < MIN_SWAPPINESS || swappiness > MAX_SWAPPINESS)
-                               return -EINVAL;
-                       break;
-               case MEMORY_RECLAIM_SWAPPINESS_MAX:
-                       swappiness = SWAPPINESS_ANON_ONLY;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       reclaim_options = MEMCG_RECLAIM_MAY_SWAP | MEMCG_RECLAIM_PROACTIVE;
-       while (nr_reclaimed < nr_to_reclaim) {
-               /* Will converge on zero, but reclaim enforces a minimum */
-               unsigned long batch_size = (nr_to_reclaim - nr_reclaimed) / 4;
-               unsigned long reclaimed;
-
-               if (signal_pending(current))
-                       return -EINTR;
-
-               /*
-                * This is the final attempt, drain percpu lru caches in the
-                * hope of introducing more evictable pages for
-                * try_to_free_mem_cgroup_pages().
-                */
-               if (!nr_retries)
-                       lru_add_drain_all();
-
-               reclaimed = try_to_free_mem_cgroup_pages(memcg,
-                                       batch_size, GFP_KERNEL,
-                                       reclaim_options,
-                                       swappiness == -1 ? NULL : &swappiness);
-
-               if (!reclaimed && !nr_retries--)
-                       return -EAGAIN;
+       int ret;
 
-               nr_reclaimed += reclaimed;
-       }
+       ret = user_proactive_reclaim(buf, memcg, NULL);
+       if (ret)
+               return ret;
 
        return nbytes;
 }
index 85ffff3b4d24cccb6ef06fea3061da5e88bcabf9..9702ee5aa65d5f11a1302688fa91b073a678060c 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/rculist_nulls.h>
 #include <linux/random.h>
 #include <linux/mmu_notifier.h>
+#include <linux/parser.h>
 
 #include <asm/tlbflush.h>
 #include <asm/div64.h>
@@ -6714,6 +6715,15 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
 
        return nr_reclaimed;
 }
+#else
+unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
+                                          unsigned long nr_pages,
+                                          gfp_t gfp_mask,
+                                          unsigned int reclaim_options,
+                                          int *swappiness)
+{
+       return 0;
+}
 #endif
 
 static void kswapd_age_node(struct pglist_data *pgdat, struct scan_control *sc)
@@ -7708,6 +7718,94 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
 
        return ret;
 }
+
+enum {
+       MEMORY_RECLAIM_SWAPPINESS = 0,
+       MEMORY_RECLAIM_SWAPPINESS_MAX,
+       MEMORY_RECLAIM_NULL,
+};
+static const match_table_t tokens = {
+       { MEMORY_RECLAIM_SWAPPINESS, "swappiness=%d"},
+       { MEMORY_RECLAIM_SWAPPINESS_MAX, "swappiness=max"},
+       { MEMORY_RECLAIM_NULL, NULL },
+};
+
+int user_proactive_reclaim(char *buf, struct mem_cgroup *memcg, pg_data_t *pgdat)
+{
+       unsigned int nr_retries = MAX_RECLAIM_RETRIES;
+       unsigned long nr_to_reclaim, nr_reclaimed = 0;
+       int swappiness = -1;
+       char *old_buf, *start;
+       substring_t args[MAX_OPT_ARGS];
+
+       if (!buf || (!memcg && !pgdat))
+               return -EINVAL;
+
+       buf = strstrip(buf);
+
+       old_buf = buf;
+       nr_to_reclaim = memparse(buf, &buf) / PAGE_SIZE;
+       if (buf == old_buf)
+               return -EINVAL;
+
+       buf = strstrip(buf);
+
+       while ((start = strsep(&buf, " ")) != NULL) {
+               if (!strlen(start))
+                       continue;
+               switch (match_token(start, tokens, args)) {
+               case MEMORY_RECLAIM_SWAPPINESS:
+                       if (match_int(&args[0], &swappiness))
+                               return -EINVAL;
+                       if (swappiness < MIN_SWAPPINESS ||
+                           swappiness > MAX_SWAPPINESS)
+                               return -EINVAL;
+                       break;
+               case MEMORY_RECLAIM_SWAPPINESS_MAX:
+                       swappiness = SWAPPINESS_ANON_ONLY;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       while (nr_reclaimed < nr_to_reclaim) {
+               /* Will converge on zero, but reclaim enforces a minimum */
+               unsigned long batch_size = (nr_to_reclaim - nr_reclaimed) / 4;
+               unsigned long reclaimed;
+
+               if (signal_pending(current))
+                       return -EINTR;
+
+               /*
+                * This is the final attempt, drain percpu lru caches in the
+                * hope of introducing more evictable pages.
+                */
+               if (!nr_retries)
+                       lru_add_drain_all();
+
+               if (memcg) {
+                       unsigned int reclaim_options;
+
+                       reclaim_options = MEMCG_RECLAIM_MAY_SWAP |
+                                         MEMCG_RECLAIM_PROACTIVE;
+                       reclaimed = try_to_free_mem_cgroup_pages(memcg,
+                                                batch_size, GFP_KERNEL,
+                                                reclaim_options,
+                                                swappiness == -1 ? NULL : &swappiness);
+               } else {
+                       return -EINVAL;
+               }
+
+               if (!reclaimed && !nr_retries--)
+                       return -EAGAIN;
+
+               nr_reclaimed += reclaimed;
+       }
+
+       return 0;
+}
+
 #endif
 
 /**