| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/08 05:06 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA",
"CPUSETS"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr"
],
"Reasoning": "The patch modifies the core hugetlb logic for gathering surplus huge pages to respect mempolicies and cpusets. It adds a new selftest to verify this behavior. Fuzzing this is important to ensure no regressions or memory leaks occur when allocating surplus huge pages under different mempolicies.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/08 05:06 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 2726d77d89a6b914f300e852efe1a8f6fa20219c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 8 05:06:29 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex f24bf49be047e3..f5e678e99721e1 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -2255,6 +2255,23 @@ static nodemask_t *policy_mbind_nodemask(gfp_t gfp)\n \treturn NULL;\n }\n \n+static unsigned int allowed_mems_nr(struct hstate *h)\n+{\n+\tint node;\n+\tunsigned int nr = 0;\n+\tnodemask_t *mbind_nodemask;\n+\tunsigned int *array = h-\u003efree_huge_pages_node;\n+\tgfp_t gfp_mask = htlb_alloc_mask(h);\n+\n+\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\n+\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\n+\t\tif (!mbind_nodemask || node_isset(node, *mbind_nodemask))\n+\t\t\tnr += array[node];\n+\t}\n+\n+\treturn nr;\n+}\n+\n /*\n * Increase the hugetlb pool such that it can accommodate a reservation\n * of size 'delta'.\n@@ -2277,7 +2294,8 @@ static int gather_surplus_pages(struct hstate *h, long delta)\n \t\talloc_nodemask = cpuset_current_mems_allowed;\n \n \tlockdep_assert_held(\u0026hugetlb_lock);\n-\tneeded = (h-\u003eresv_huge_pages + delta) - h-\u003efree_huge_pages;\n+\tneeded = max_t(long, (delta - allowed_mems_nr(h)),\n+\t\t\t((h-\u003eresv_huge_pages + delta) - h-\u003efree_huge_pages));\n \tif (needed \u003c= 0) {\n \t\th-\u003eresv_huge_pages += delta;\n \t\treturn 0;\n@@ -2311,8 +2329,9 @@ static int gather_surplus_pages(struct hstate *h, long delta)\n \t * because either resv_huge_pages or free_huge_pages may have changed.\n \t */\n \tspin_lock_irq(\u0026hugetlb_lock);\n-\tneeded = (h-\u003eresv_huge_pages + delta) -\n-\t\t\t(h-\u003efree_huge_pages + allocated);\n+\tneeded = max_t(long, ((delta - allowed_mems_nr(h)) - allocated),\n+\t\t\t((h-\u003eresv_huge_pages + delta) -\n+\t\t\t\t(h-\u003efree_huge_pages + allocated)));\n \tif (needed \u003e 0) {\n \t\tif (alloc_ok)\n \t\t\tgoto retry;\n@@ -4513,23 +4532,6 @@ static int __init hugepage_alloc_threads_setup(char *s)\n }\n __setup(\"hugepage_alloc_threads=\", hugepage_alloc_threads_setup);\n \n-static unsigned int allowed_mems_nr(struct hstate *h)\n-{\n-\tint node;\n-\tunsigned int nr = 0;\n-\tnodemask_t *mbind_nodemask;\n-\tunsigned int *array = h-\u003efree_huge_pages_node;\n-\tgfp_t gfp_mask = htlb_alloc_mask(h);\n-\n-\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\n-\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\n-\t\tif (!mbind_nodemask || node_isset(node, *mbind_nodemask))\n-\t\t\tnr += array[node];\n-\t}\n-\n-\treturn nr;\n-}\n-\n void hugetlb_report_meminfo(struct seq_file *m)\n {\n \tstruct hstate *h;\ndiff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile\nindex cd24596cdd27ed..40de0938f3937c 100644\n--- a/tools/testing/selftests/mm/Makefile\n+++ b/tools/testing/selftests/mm/Makefile\n@@ -106,6 +106,7 @@ TEST_GEN_FILES += guard-regions\n TEST_GEN_FILES += merge\n TEST_GEN_FILES += rmap\n TEST_GEN_FILES += folio_split_race_test\n+TEST_GEN_FILES += hugetlb_surplus_mempolicy\n \n ifneq ($(ARCH),arm64)\n TEST_GEN_FILES += soft-dirty\n@@ -260,6 +261,8 @@ $(OUTPUT)/migration: LDLIBS += -lnuma\n \n $(OUTPUT)/rmap: LDLIBS += -lnuma\n \n+$(OUTPUT)/hugetlb_surplus_mempolicy: LDLIBS += -lnuma\n+\n local_config.mk local_config.h: check_config.sh\n \tCC=\"$(CC)\" CFLAGS=\"$(CFLAGS)\" ./check_config.sh\n \ndiff --git a/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c\nnew file mode 100644\nindex 00000000000000..e737befcd06867\n--- /dev/null\n+++ b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c\n@@ -0,0 +1,491 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * hugetlb_surplus_mempolicy\n+ *\n+ * Reserving surplus hugepages within mempolicies is quite tricky due to\n+ * the transient nature of cpusets and mempolicies. As such, these tests\n+ * do not cover all edge cases, but rather focus on what the kernel can\n+ * currently do to reserve surplus hugepages in the presence of cpusets\n+ * and mempolicies to help check for regressions in this behavior.\n+ */\n+\n+#define _GNU_SOURCE\n+#include \u003cerrno.h\u003e\n+#include \u003cnuma.h\u003e\n+#include \u003cpthread.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"vm_util.h\"\n+#include \"kselftest.h\"\n+\n+#define HPSIZE_BYTES default_huge_page_size()\n+#define HPSIZE_KB default_huge_page_size() \u003e\u003e 10\n+#define GLOBAL_SYS_HP_PATH \"/sys/kernel/mm/hugepages/hugepages-%lukB/%s\"\n+#define NODE_SYS_HP_PATH \"/sys/devices/system/node/node%u/hugepages/hugepages-%lukB/%s\"\n+\n+struct bitmask **nodemasks;\n+int *nodeids;\n+\n+pthread_t *threads;\n+struct thread_args {\n+\tstruct bitmask *my_nodemask;\n+\tint to_reserve;\n+};\n+struct thread_args *per_thread_args;\n+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;\n+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n+int wake_cond;\n+\n+char *nr_overcommit_hugepages_path;\n+char *g_free_hugepages_path;\n+char *g_nr_hugepages_path;\n+char *g_resv_hugepages_path;\n+char *g_surplus_hugepages_path;\n+char *n0_free_hugepages_path;\n+char *n0_nr_hugepages_path;\n+char *n0_surplus_hugepages_path;\n+char *n1_free_hugepages_path;\n+char *n1_nr_hugepages_path;\n+char *n1_surplus_hugepages_path;\n+\n+unsigned long g_free_hugepages, g_nr_hugepages;\n+unsigned long g_resv_hugepages, g_surplus_hugepages;\n+unsigned long n0_free_hugepages, n0_nr_hugepages, n0_surplus_hugepages;\n+unsigned long n1_free_hugepages, n1_nr_hugepages, n1_surplus_hugepages;\n+unsigned long orig_n0_nr_hugepages, orig_n1_nr_hugepages;\n+unsigned long orig_nr_overcommit_hugepages;\n+\n+\n+/* setup_paths\n+ *\n+ * Helper function to create strings for the various hugetlb page sysfs\n+ * paths. The strings are used to read from and write to the sysfs files.\n+ */\n+static void setup_paths(void)\n+{\n+\tasprintf(\u0026nr_overcommit_hugepages_path,\n+\t\t\t\"/proc/sys/vm/nr_overcommit_hugepages\");\n+\tasprintf(\u0026g_free_hugepages_path, GLOBAL_SYS_HP_PATH,\n+\t\t\tHPSIZE_KB, \"free_hugepages\");\n+\tasprintf(\u0026g_nr_hugepages_path, GLOBAL_SYS_HP_PATH,\n+\t\t\tHPSIZE_KB, \"nr_hugepages\");\n+\tasprintf(\u0026g_resv_hugepages_path, GLOBAL_SYS_HP_PATH,\n+\t\t\tHPSIZE_KB, \"resv_hugepages\");\n+\tasprintf(\u0026g_surplus_hugepages_path, GLOBAL_SYS_HP_PATH,\n+\t\t\tHPSIZE_KB, \"surplus_hugepages\");\n+\tasprintf(\u0026n0_free_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],\n+\t\t\tHPSIZE_KB, \"free_hugepages\");\n+\tasprintf(\u0026n0_nr_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],\n+\t\t\tHPSIZE_KB, \"nr_hugepages\");\n+\tasprintf(\u0026n0_surplus_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],\n+\t\t\tHPSIZE_KB, \"surplus_hugepages\");\n+\tasprintf(\u0026n1_free_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],\n+\t\t\tHPSIZE_KB, \"free_hugepages\");\n+\tasprintf(\u0026n1_nr_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],\n+\t\t\tHPSIZE_KB, \"nr_hugepages\");\n+\tasprintf(\u0026n1_surplus_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],\n+\t\t\tHPSIZE_KB, \"surplus_hugepages\");\n+}\n+\n+/* get_hugepage_stats\n+ *\n+ * Helper function to simply grab a bunch of the hugetlb page metrics in sysfs\n+ */\n+static void get_hugepage_stats(void)\n+{\n+\tread_sysfs(g_free_hugepages_path, \u0026g_free_hugepages);\n+\tread_sysfs(g_nr_hugepages_path, \u0026g_nr_hugepages);\n+\tread_sysfs(g_resv_hugepages_path, \u0026g_resv_hugepages);\n+\tread_sysfs(g_surplus_hugepages_path, \u0026g_surplus_hugepages);\n+\tread_sysfs(n0_free_hugepages_path, \u0026n0_free_hugepages);\n+\tread_sysfs(n0_nr_hugepages_path, \u0026n0_nr_hugepages);\n+\tread_sysfs(n0_surplus_hugepages_path, \u0026n0_surplus_hugepages);\n+\tread_sysfs(n1_free_hugepages_path, \u0026n1_free_hugepages);\n+\tread_sysfs(n1_nr_hugepages_path, \u0026n1_nr_hugepages);\n+\tread_sysfs(n1_surplus_hugepages_path, \u0026n1_surplus_hugepages);\n+}\n+\n+/* save_hugepage_configs\n+ *\n+ * Helper function to save the current state of the hugepage configs so this\n+ * test suite doesn't clobber configs needed for other tests.\n+ */\n+static void save_hugepage_configs(void)\n+{\n+\tread_sysfs(n0_nr_hugepages_path, \u0026orig_n0_nr_hugepages);\n+\tread_sysfs(n1_nr_hugepages_path, \u0026orig_n1_nr_hugepages);\n+\tread_sysfs(nr_overcommit_hugepages_path, \u0026orig_nr_overcommit_hugepages);\n+}\n+\n+/* restore_hugepage_configs\n+ *\n+ * Helper function to restore the state of hugepage configs before this test\n+ * was ran.\n+ */\n+static void restore_hugepage_configs(void)\n+{\n+\twrite_sysfs(n0_nr_hugepages_path, orig_n0_nr_hugepages);\n+\twrite_sysfs(n1_nr_hugepages_path, orig_n1_nr_hugepages);\n+\twrite_sysfs(nr_overcommit_hugepages_path, orig_nr_overcommit_hugepages);\n+}\n+\n+/* reset_hugepages\n+ *\n+ * Helper function to reset static hugetlb page reservations to 0.\n+ * Used to get back to a clear state between tests.\n+ */\n+static void reset_hugepages(void)\n+{\n+\twrite_sysfs(nr_overcommit_hugepages_path, 0);\n+\twrite_sysfs(g_nr_hugepages_path, 0);\n+\twrite_sysfs(n0_nr_hugepages_path, 0);\n+\twrite_sysfs(n1_nr_hugepages_path, 0);\n+}\n+\n+/* can_run\n+ *\n+ * Does sanity checking first to make sure the tests can even run.\n+ */\n+static void check_requirements(void)\n+{\n+\tif (geteuid() != 0)\n+\t\tksft_exit_skip(\"Please run the test as root.\\n\");\n+\n+\tif (numa_available() == -1)\n+\t\tksft_exit_skip(\"Numa is unavailable.\\n\");\n+\n+\tif (numa_num_configured_nodes() \u003c 2)\n+\t\tksft_exit_skip(\"Not enough nodes to test.\\n\");\n+\n+\tif (numa_num_task_nodes() \u003c 2)\n+\t\tksft_exit_skip(\"Current mempolicy is too restrictive.\\n\");\n+}\n+\n+static void cleanup(char *err_msg)\n+{\n+\tfree(per_thread_args);\n+\tfree(threads);\n+\tfree(nodeids);\n+\tfree(nodemasks);\n+\tfree(nr_overcommit_hugepages_path);\n+\tfree(g_free_hugepages_path);\n+\tfree(g_nr_hugepages_path);\n+\tfree(g_resv_hugepages_path);\n+\tfree(g_surplus_hugepages_path);\n+\tfree(n0_free_hugepages_path);\n+\tfree(n0_nr_hugepages_path);\n+\tfree(n0_surplus_hugepages_path);\n+\tfree(n1_free_hugepages_path);\n+\tfree(n1_nr_hugepages_path);\n+\tfree(n1_surplus_hugepages_path);\n+\tif (err_msg)\n+\t\tksft_exit_fail_msg(err_msg);\n+}\n+\n+/* setup_node_info\n+ *\n+ * Creates the bitmasks used to isolate test runners and their hugetlb page\n+ * reservations.\n+ */\n+static void setup_node_info(void)\n+{\n+\tint i;\n+\tint ith_nodemask = 0;\n+\n+\tnodeids = calloc(2, sizeof(int));\n+\tnodemasks = calloc(2, sizeof(struct bitmask *));\n+\n+\tif (!nodemasks || !nodeids)\n+\t\tcleanup(\"Failed to allocate nodemasks or nodeids.\");\n+\n+\t/* Walk the nodes available to us. Create two bitmasks, one of the\n+\t * index of the first node available to us, and the second of the next\n+\t * node available to us.\n+\t */\n+\tfor (i = 0; i \u003c numa_num_task_nodes(); i++) {\n+\t\tif (numa_bitmask_isbitset(numa_get_mems_allowed(), i)) {\n+\t\t\tnodeids[ith_nodemask] = i;\n+\t\t\tnodemasks[ith_nodemask++] = numa_bitmask_setbit(\n+\t\t\t\t\tnuma_allocate_nodemask(), i);\n+\t\t}\n+\t}\n+\tif (ith_nodemask != 2 || !nodemasks[0] || !nodemasks[1])\n+\t\tcleanup(\"Failed to create nodemasks.\");\n+}\n+\n+/* setup_threads\n+ *\n+ * Helper function to setup space for threads.\n+ */\n+static void setup_threads(void)\n+{\n+\tper_thread_args = calloc(2, sizeof(struct thread_args));\n+\tif (!per_thread_args)\n+\t\tcleanup(\"calloc thread args.\");\n+\n+\tthreads = calloc(2, sizeof(pthread_t));\n+\tif (!threads)\n+\t\tcleanup(\"calloc threads.\");\n+}\n+\n+/* reserve_hugepage\n+ *\n+ * Helper function to reserve a hugetlb page\n+ */\n+static unsigned long *reserve_hugepage(void)\n+{\n+\treturn (unsigned long *) mmap(NULL, HPSIZE_BYTES, PROT_READ | PROT_WRITE,\n+\t\tMAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);\n+}\n+\n+/* thread_work\n+ *\n+ * Test runners. Performs the work of reserving and freeing hugetlb pages.\n+ */\n+static void *thread_work(void *arg)\n+{\n+\tstruct thread_args *t_args = (struct thread_args *) arg;\n+\tunsigned long **hugepages;\n+\tint i;\n+\n+\thugepages = (unsigned long **) calloc(t_args-\u003eto_reserve,\n+\t\t\t\t\t\tsizeof(unsigned long **));\n+\n+\t/* Reserve hugetlb pages on my node */\n+\tif (t_args-\u003emy_nodemask)\n+\t\tnuma_bind(t_args-\u003emy_nodemask);\n+\tfor (i = 0; i \u003c t_args-\u003eto_reserve; i++) {\n+\t\thugepages[i] = reserve_hugepage();\n+\t\t/* Tests may purposefully try to overallocate, so just\n+\t\t * fallthrough rather than error out\n+\t\t */\n+\t\tif (hugepages[i] == MAP_FAILED) {\n+\t\t\tt_args-\u003eto_reserve = i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/* Go to sleep until main thread wakes us up */\n+\tpthread_mutex_lock(\u0026mutex);\n+\twhile (!wake_cond)\n+\t\tpthread_cond_wait(\u0026cond, \u0026mutex);\n+\tpthread_mutex_unlock(\u0026mutex);\n+\n+\t/* Try to free those hugetlb pages */\n+\tfor (i = 0; i \u003c t_args-\u003eto_reserve; i++) {\n+\t\tif (munmap(hugepages[i], HPSIZE_BYTES) \u003c 0)\n+\t\t\tksft_perror(\"munmap() failed! Check for leaked hugetlb pages!\\n\");\n+\t}\n+\tfree(hugepages);\n+\treturn NULL;\n+}\n+\n+/* wake_children\n+ *\n+ * Helper function to wake children threads.\n+ */\n+static void wake_children(void)\n+{\n+\tpthread_mutex_lock(\u0026mutex);\n+\twake_cond = 1;\n+\tpthread_cond_broadcast(\u0026cond);\n+\tpthread_mutex_unlock(\u0026mutex);\n+}\n+\n+/* test1\n+ *\n+ * Sanity checking, attempt to reserve a surplus hugetlb page anywhere.\n+ */\n+static void test1(void)\n+{\n+\treset_hugepages();\n+\n+\twrite_sysfs(nr_overcommit_hugepages_path, 1);\n+\tper_thread_args[0].my_nodemask = NULL;\n+\tper_thread_args[0].to_reserve = 1;\n+\n+\tpthread_create(\u0026threads[0], NULL, thread_work, \u0026per_thread_args[0]);\n+\n+\tusleep(500000);\n+\n+\tget_hugepage_stats();\n+\tksft_test_result((g_free_hugepages == 1 \u0026\u0026 g_nr_hugepages == 1 \u0026\u0026\n+\t\t\t g_resv_hugepages == 1 \u0026\u0026 g_surplus_hugepages == 1) \u0026\u0026\n+\t\t\t ((n0_free_hugepages == 1 \u0026\u0026 n0_nr_hugepages == 1 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 1 \u0026\u0026 n1_free_hugepages == 0 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 0 \u0026\u0026 n1_surplus_hugepages == 0) ||\n+\t\t\t (n0_free_hugepages == 0 \u0026\u0026 n0_nr_hugepages == 0 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 0 \u0026\u0026 n1_free_hugepages == 1 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 1 \u0026\u0026 n1_surplus_hugepages == 1)),\n+\t\t\t \"Reserve 1 surplus hugepage anywhere\\n\");\n+\n+\twake_children();\n+\tpthread_join(threads[0], NULL);\n+\twake_cond = 0;\n+\treset_hugepages();\n+}\n+\n+/* test2\n+ *\n+ * Sanity checking, attempt to reserve a surplus hugetlb page with\n+ * a mempolicy.\n+ */\n+static void test2(void)\n+{\n+\treset_hugepages();\n+\n+\twrite_sysfs(nr_overcommit_hugepages_path, 1);\n+\tper_thread_args[0].my_nodemask = nodemasks[0];\n+\tper_thread_args[0].to_reserve = 1;\n+\n+\tpthread_create(\u0026threads[0], NULL, thread_work, \u0026per_thread_args[0]);\n+\n+\tusleep(500000);\n+\n+\tget_hugepage_stats();\n+\tksft_test_result(g_free_hugepages == 1 \u0026\u0026 g_nr_hugepages == 1 \u0026\u0026\n+\t\t\t g_resv_hugepages == 1 \u0026\u0026 g_surplus_hugepages == 1 \u0026\u0026\n+\t\t\t n0_free_hugepages == 1 \u0026\u0026 n0_nr_hugepages == 1 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 1 \u0026\u0026 n1_free_hugepages == 0 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 0 \u0026\u0026 n1_surplus_hugepages == 0,\n+\t\t\t \"Reserve 1 surplus hugepage on node0\\n\");\n+\n+\twake_children();\n+\tpthread_join(threads[0], NULL);\n+\twake_cond = 0;\n+\treset_hugepages();\n+}\n+\n+/* test3\n+ *\n+ * Set a static hugepage and reserve off node\n+ */\n+static void test3(void)\n+{\n+\treset_hugepages();\n+\n+\twrite_sysfs(nr_overcommit_hugepages_path, 1);\n+\twrite_sysfs(n0_nr_hugepages_path, 1);\n+\n+\tper_thread_args[0].my_nodemask = nodemasks[0];\n+\tper_thread_args[0].to_reserve = 0;\n+\tper_thread_args[1].my_nodemask = nodemasks[1];\n+\tper_thread_args[1].to_reserve = 1;\n+\n+\tpthread_create(\u0026threads[0], NULL, thread_work, \u0026per_thread_args[0]);\n+\tpthread_create(\u0026threads[1], NULL, thread_work, \u0026per_thread_args[1]);\n+\n+\tusleep(500000);\n+\n+\tget_hugepage_stats();\n+\tksft_test_result(g_free_hugepages == 2 \u0026\u0026 g_nr_hugepages == 2 \u0026\u0026\n+\t\t\t g_resv_hugepages == 1 \u0026\u0026 g_surplus_hugepages == 1 \u0026\u0026\n+\t\t\t n0_free_hugepages == 1 \u0026\u0026 n0_nr_hugepages == 1 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 0 \u0026\u0026 n1_free_hugepages == 1 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 1 \u0026\u0026 n1_surplus_hugepages == 1,\n+\t\t\t \"Set 1 static hugepage on node0, reserve surplus hugepage on node 1\\n\");\n+\n+\twake_children();\n+\tpthread_join(threads[0], NULL);\n+\tpthread_join(threads[1], NULL);\n+\twake_cond = 0;\n+\treset_hugepages();\n+}\n+\n+/* test4\n+ *\n+ * Reserve static hugepage on node0, reserve surplus hugepage on node1\n+ */\n+static void test4(void)\n+{\n+\treset_hugepages();\n+\n+\twrite_sysfs(nr_overcommit_hugepages_path, 1);\n+\twrite_sysfs(n0_nr_hugepages_path, 1);\n+\n+\tper_thread_args[0].my_nodemask = nodemasks[0];\n+\tper_thread_args[0].to_reserve = 1;\n+\tper_thread_args[1].my_nodemask = nodemasks[1];\n+\tper_thread_args[1].to_reserve = 1;\n+\n+\tpthread_create(\u0026threads[0], NULL, thread_work, \u0026per_thread_args[0]);\n+\tpthread_create(\u0026threads[1], NULL, thread_work, \u0026per_thread_args[1]);\n+\n+\tusleep(500000);\n+\n+\tget_hugepage_stats();\n+\tksft_test_result(g_free_hugepages == 2 \u0026\u0026 g_nr_hugepages == 2 \u0026\u0026\n+\t\t\t g_resv_hugepages == 2 \u0026\u0026 g_surplus_hugepages == 1 \u0026\u0026\n+\t\t\t n0_free_hugepages == 1 \u0026\u0026 n0_nr_hugepages == 1 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 0 \u0026\u0026 n1_free_hugepages == 1 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 1 \u0026\u0026 n1_surplus_hugepages == 1,\n+\t\t\t \"Reserve 1 static hugepage on node0, reserve surplus hugepage on node 1\\n\");\n+\n+\twake_children();\n+\tpthread_join(threads[0], NULL);\n+\tpthread_join(threads[1], NULL);\n+\twake_cond = 0;\n+\treset_hugepages();\n+}\n+\n+/* test5\n+ *\n+ * Reserve static hugepage on node0, reserve surplus hugepage on node1 and\n+ * fail to over allocate another.\n+ */\n+static void test5(void)\n+{\n+\treset_hugepages();\n+\n+\twrite_sysfs(nr_overcommit_hugepages_path, 1);\n+\twrite_sysfs(n0_nr_hugepages_path, 1);\n+\n+\tper_thread_args[0].my_nodemask = nodemasks[0];\n+\tper_thread_args[0].to_reserve = 1;\n+\tper_thread_args[1].my_nodemask = nodemasks[1];\n+\tper_thread_args[1].to_reserve = 2;\n+\n+\tpthread_create(\u0026threads[0], NULL, thread_work, \u0026per_thread_args[0]);\n+\tpthread_create(\u0026threads[1], NULL, thread_work, \u0026per_thread_args[1]);\n+\n+\tusleep(500000);\n+\n+\tget_hugepage_stats();\n+\tksft_test_result(g_free_hugepages == 2 \u0026\u0026 g_nr_hugepages == 2 \u0026\u0026\n+\t\t\t g_resv_hugepages == 2 \u0026\u0026 g_surplus_hugepages == 1 \u0026\u0026\n+\t\t\t n0_free_hugepages == 1 \u0026\u0026 n0_nr_hugepages == 1 \u0026\u0026\n+\t\t\t n0_surplus_hugepages == 0 \u0026\u0026 n1_free_hugepages == 1 \u0026\u0026\n+\t\t\t n1_nr_hugepages == 1 \u0026\u0026 n1_surplus_hugepages == 1,\n+\t\t\t \"Intentionally overallocate and fail due to nr_overcommit_hugepages limit.\\n\");\n+\n+\twake_children();\n+\tpthread_join(threads[0], NULL);\n+\tpthread_join(threads[1], NULL);\n+\twake_cond = 0;\n+\treset_hugepages();\n+\n+}\n+\n+int main(void)\n+{\n+\tksft_print_header();\n+\tksft_set_plan(5);\n+\n+\tcheck_requirements();\n+\tsetup_threads();\n+\tsetup_node_info();\n+\tsetup_paths();\n+\tsave_hugepage_configs();\n+\twake_cond = 0;\n+\n+\ttest1();\n+\ttest2();\n+\ttest3();\n+\ttest4();\n+\ttest5();\n+\n+\trestore_hugepage_configs();\n+\tksft_finished();\n+}\ndiff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh\nindex c17b133a81d248..cd368ce590a74c 100755\n--- a/tools/testing/selftests/mm/run_vmtests.sh\n+++ b/tools/testing/selftests/mm/run_vmtests.sh\n@@ -297,6 +297,7 @@ CATEGORY=\"hugetlb\" run_test ./hugepage-mremap\n CATEGORY=\"hugetlb\" run_test ./hugepage-vmemmap\n CATEGORY=\"hugetlb\" run_test ./hugetlb-madvise\n CATEGORY=\"hugetlb\" run_test ./hugetlb_dio\n+CATEGORY=\"hugetlb\" run_test ./hugetlb_surplus_mempolicy\n \n if [ \"${HAVE_HUGEPAGES}\" = \"1\" ]; then\n \tnr_hugepages_tmp=$(cat /proc/sys/vm/nr_hugepages)\n"
} |
| 2/1 | 2026/07/08 05:06 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA",
"CPUSETS"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr"
],
"Reasoning": "The patch modifies the core hugetlb logic for gathering surplus huge pages to respect mempolicies and cpusets. It adds a new selftest to verify this behavior. Fuzzing this is important to ensure no regressions or memory leaks occur when allocating surplus huge pages under different mempolicies.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 2726d77d89a6b914f300e852efe1a8f6fa20219c
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 8 05:06:29 2026 +0000
syz-cluster: applied patch under review
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f24bf49be047e3..f5e678e99721e1 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2255,6 +2255,23 @@ static nodemask_t *policy_mbind_nodemask(gfp_t gfp)
return NULL;
}
+static unsigned int allowed_mems_nr(struct hstate *h)
+{
+ int node;
+ unsigned int nr = 0;
+ nodemask_t *mbind_nodemask;
+ unsigned int *array = h->free_huge_pages_node;
+ gfp_t gfp_mask = htlb_alloc_mask(h);
+
+ mbind_nodemask = policy_mbind_nodemask(gfp_mask);
+ for_each_node_mask(node, cpuset_current_mems_allowed) {
+ if (!mbind_nodemask || node_isset(node, *mbind_nodemask))
+ nr += array[node];
+ }
+
+ return nr;
+}
+
/*
* Increase the hugetlb pool such that it can accommodate a reservation
* of size 'delta'.
@@ -2277,7 +2294,8 @@ static int gather_surplus_pages(struct hstate *h, long delta)
alloc_nodemask = cpuset_current_mems_allowed;
lockdep_assert_held(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) - h->free_huge_pages;
+ needed = max_t(long, (delta - allowed_mems_nr(h)),
+ ((h->resv_huge_pages + delta) - h->free_huge_pages));
if (needed <= 0) {
h->resv_huge_pages += delta;
return 0;
@@ -2311,8 +2329,9 @@ static int gather_surplus_pages(struct hstate *h, long delta)
* because either resv_huge_pages or free_huge_pages may have changed.
*/
spin_lock_irq(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) -
- (h->free_huge_pages + allocated);
+ needed = max_t(long, ((delta - allowed_mems_nr(h)) - allocated),
+ ((h->resv_huge_pages + delta) -
+ (h->free_huge_pages + allocated)));
if (needed > 0) {
if (alloc_ok)
goto retry;
@@ -4513,23 +4532,6 @@ static int __init hugepage_alloc_threads_setup(char *s)
}
__setup("hugepage_alloc_threads=", hugepage_alloc_threads_setup);
-static unsigned int allowed_mems_nr(struct hstate *h)
-{
- int node;
- unsigned int nr = 0;
- nodemask_t *mbind_nodemask;
- unsigned int *array = h->free_huge_pages_node;
- gfp_t gfp_mask = htlb_alloc_mask(h);
-
- mbind_nodemask = policy_mbind_nodemask(gfp_mask);
- for_each_node_mask(node, cpuset_current_mems_allowed) {
- if (!mbind_nodemask || node_isset(node, *mbind_nodemask))
- nr += array[node];
- }
-
- return nr;
-}
-
void hugetlb_report_meminfo(struct seq_file *m)
{
struct hstate *h;
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index cd24596cdd27ed..40de0938f3937c 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -106,6 +106,7 @@ TEST_GEN_FILES += guard-regions
TEST_GEN_FILES += merge
TEST_GEN_FILES += rmap
TEST_GEN_FILES += folio_split_race_test
+TEST_GEN_FILES += hugetlb_surplus_mempolicy
ifneq ($(ARCH),arm64)
TEST_GEN_FILES += soft-dirty
@@ -260,6 +261,8 @@ $(OUTPUT)/migration: LDLIBS += -lnuma
$(OUTPUT)/rmap: LDLIBS += -lnuma
+$(OUTPUT)/hugetlb_surplus_mempolicy: LDLIBS += -lnuma
+
local_config.mk local_config.h: check_config.sh
CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh
diff --git a/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c
new file mode 100644
index 00000000000000..e737befcd06867
--- /dev/null
+++ b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * hugetlb_surplus_mempolicy
+ *
+ * Reserving surplus hugepages within mempolicies is quite tricky due to
+ * the transient nature of cpusets and mempolicies. As such, these tests
+ * do not cover all edge cases, but rather focus on what the kernel can
+ * currently do to reserve surplus hugepages in the presence of cpusets
+ * and mempolicies to help check for regressions in this behavior.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <numa.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "vm_util.h"
+#include "kselftest.h"
+
+#define HPSIZE_BYTES default_huge_page_size()
+#define HPSIZE_KB default_huge_page_size() >> 10
+#define GLOBAL_SYS_HP_PATH "/sys/kernel/mm/hugepages/hugepages-%lukB/%s"
+#define NODE_SYS_HP_PATH "/sys/devices/system/node/node%u/hugepages/hugepages-%lukB/%s"
+
+struct bitmask **nodemasks;
+int *nodeids;
+
+pthread_t *threads;
+struct thread_args {
+ struct bitmask *my_nodemask;
+ int to_reserve;
+};
+struct thread_args *per_thread_args;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+int wake_cond;
+
+char *nr_overcommit_hugepages_path;
+char *g_free_hugepages_path;
+char *g_nr_hugepages_path;
+char *g_resv_hugepages_path;
+char *g_surplus_hugepages_path;
+char *n0_free_hugepages_path;
+char *n0_nr_hugepages_path;
+char *n0_surplus_hugepages_path;
+char *n1_free_hugepages_path;
+char *n1_nr_hugepages_path;
+char *n1_surplus_hugepages_path;
+
+unsigned long g_free_hugepages, g_nr_hugepages;
+unsigned long g_resv_hugepages, g_surplus_hugepages;
+unsigned long n0_free_hugepages, n0_nr_hugepages, n0_surplus_hugepages;
+unsigned long n1_free_hugepages, n1_nr_hugepages, n1_surplus_hugepages;
+unsigned long orig_n0_nr_hugepages, orig_n1_nr_hugepages;
+unsigned long orig_nr_overcommit_hugepages;
+
+
+/* setup_paths
+ *
+ * Helper function to create strings for the various hugetlb page sysfs
+ * paths. The strings are used to read from and write to the sysfs files.
+ */
+static void setup_paths(void)
+{
+ asprintf(&nr_overcommit_hugepages_path,
+ "/proc/sys/vm/nr_overcommit_hugepages");
+ asprintf(&g_free_hugepages_path, GLOBAL_SYS_HP_PATH,
+ HPSIZE_KB, "free_hugepages");
+ asprintf(&g_nr_hugepages_path, GLOBAL_SYS_HP_PATH,
+ HPSIZE_KB, "nr_hugepages");
+ asprintf(&g_resv_hugepages_path, GLOBAL_SYS_HP_PATH,
+ HPSIZE_KB, "resv_hugepages");
+ asprintf(&g_surplus_hugepages_path, GLOBAL_SYS_HP_PATH,
+ HPSIZE_KB, "surplus_hugepages");
+ asprintf(&n0_free_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],
+ HPSIZE_KB, "free_hugepages");
+ asprintf(&n0_nr_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],
+ HPSIZE_KB, "nr_hugepages");
+ asprintf(&n0_surplus_hugepages_path, NODE_SYS_HP_PATH, nodeids[0],
+ HPSIZE_KB, "surplus_hugepages");
+ asprintf(&n1_free_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],
+ HPSIZE_KB, "free_hugepages");
+ asprintf(&n1_nr_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],
+ HPSIZE_KB, "nr_hugepages");
+ asprintf(&n1_surplus_hugepages_path, NODE_SYS_HP_PATH, nodeids[1],
+ HPSIZE_KB, "surplus_hugepages");
+}
+
+/* get_hugepage_stats
+ *
+ * Helper function to simply grab a bunch of the hugetlb page metrics in sysfs
+ */
+static void get_hugepage_stats(void)
+{
+ read_sysfs(g_free_hugepages_path, &g_free_hugepages);
+ read_sysfs(g_nr_hugepages_path, &g_nr_hugepages);
+ read_sysfs(g_resv_hugepages_path, &g_resv_hugepages);
+ read_sysfs(g_surplus_hugepages_path, &g_surplus_hugepages);
+ read_sysfs(n0_free_hugepages_path, &n0_free_hugepages);
+ read_sysfs(n0_nr_hugepages_path, &n0_nr_hugepages);
+ read_sysfs(n0_surplus_hugepages_path, &n0_surplus_hugepages);
+ read_sysfs(n1_free_hugepages_path, &n1_free_hugepages);
+ read_sysfs(n1_nr_hugepages_path, &n1_nr_hugepages);
+ read_sysfs(n1_surplus_hugepages_path, &n1_surplus_hugepages);
+}
+
+/* save_hugepage_configs
+ *
+ * Helper function to save the current state of the hugepage configs so this
+ * test suite doesn't clobber configs needed for other tests.
+ */
+static void save_hugepage_configs(void)
+{
+ read_sysfs(n0_nr_hugepages_path, &orig_n0_nr_hugepages);
+ read_sysfs(n1_nr_hugepages_path, &orig_n1_nr_hugepages);
+ read_sysfs(nr_overcommit_hugepages_path, &orig_nr_overcommit_hugepages);
+}
+
+/* restore_hugepage_configs
+ *
+ * Helper function to restore the state of hugepage configs before this test
+ * was ran.
+ */
+static void restore_hugepage_configs(void)
+{
+ write_sysfs(n0_nr_hugepages_path, orig_n0_nr_hugepages);
+ write_sysfs(n1_nr_hugepages_path, orig_n1_nr_hugepages);
+ write_sysfs(nr_overcommit_hugepages_path, orig_nr_overcommit_hugepages);
+}
+
+/* reset_hugepages
+ *
+ * Helper function to reset static hugetlb page reservations to 0.
+ * Used to get back to a clear state between tests.
+ */
+static void reset_hugepages(void)
+{
+ write_sysfs(nr_overcommit_hugepages_path, 0);
+ write_sysfs(g_nr_hugepages_path, 0);
+ write_sysfs(n0_nr_hugepages_path, 0);
+ write_sysfs(n1_nr_hugepages_path, 0);
+}
+
+/* can_run
+ *
+ * Does sanity checking first to make sure the tests can even run.
+ */
+static void check_requirements(void)
+{
+ if (geteuid() != 0)
+ ksft_exit_skip("Please run the test as root.\n");
+
+ if (numa_available() == -1)
+ ksft_exit_skip("Numa is unavailable.\n");
+
+ if (numa_num_configured_nodes() < 2)
+ ksft_exit_skip("Not enough nodes to test.\n");
+
+ if (numa_num_task_nodes() < 2)
+ ksft_exit_skip("Current mempolicy is too restrictive.\n");
+}
+
+static void cleanup(char *err_msg)
+{
+ free(per_thread_args);
+ free(threads);
+ free(nodeids);
+ free(nodemasks);
+ free(nr_overcommit_hugepages_path);
+ free(g_free_hugepages_path);
+ free(g_nr_hugepages_path);
+ free(g_resv_hugepages_path);
+ free(g_surplus_hugepages_path);
+ free(n0_free_hugepages_path);
+ free(n0_nr_hugepages_path);
+ free(n0_surplus_hugepages_path);
+ free(n1_free_hugepages_path);
+ free(n1_nr_hugepages_path);
+ free(n1_surplus_hugepages_path);
+ if (err_msg)
+ ksft_exit_fail_msg(err_msg);
+}
+
+/* setup_node_info
+ *
+ * Creates the bitmasks used to isolate test runners and their hugetlb page
+ * reservations.
+ */
+static void setup_node_info(void)
+{
+ int i;
+ int ith_nodemask = 0;
+
+ nodeids = calloc(2, sizeof(int));
+ nodemasks = calloc(2, sizeof(struct bitmask *));
+
+ if (!nodemasks || !nodeids)
+ cleanup("Failed to allocate nodemasks or nodeids.");
+
+ /* Walk the nodes available to us. Create two bitmasks, one of the
+ * index of the first node available to us, and the second of the next
+ * node available to us.
+ */
+ for (i = 0; i < numa_num_task_nodes(); i++) {
+ if (numa_bitmask_isbitset(numa_get_mems_allowed(), i)) {
+ nodeids[ith_nodemask] = i;
+ nodemasks[ith_nodemask++] = numa_bitmask_setbit(
+ numa_allocate_nodemask(), i);
+ }
+ }
+ if (ith_nodemask != 2 || !nodemasks[0] || !nodemasks[1])
+ cleanup("Failed to create nodemasks.");
+}
+
+/* setup_threads
+ *
+ * Helper function to setup space for threads.
+ */
+static void setup_threads(void)
+{
+ per_thread_args = calloc(2, sizeof(struct thread_args));
+ if (!per_thread_args)
+ cleanup("calloc thread args.");
+
+ threads = calloc(2, sizeof(pthread_t));
+ if (!threads)
+ cleanup("calloc threads.");
+}
+
+/* reserve_hugepage
+ *
+ * Helper function to reserve a hugetlb page
+ */
+static unsigned long *reserve_hugepage(void)
+{
+ return (unsigned long *) mmap(NULL, HPSIZE_BYTES, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+}
+
+/* thread_work
+ *
+ * Test runners. Performs the work of reserving and freeing hugetlb pages.
+ */
+static void *thread_work(void *arg)
+{
+ struct thread_args *t_args = (struct thread_args *) arg;
+ unsigned long **hugepages;
+ int i;
+
+ hugepages = (unsigned long **) calloc(t_args->to_reserve,
+ sizeof(unsigned long **));
+
+ /* Reserve hugetlb pages on my node */
+ if (t_args->my_nodemask)
+ numa_bind(t_args->my_nodemask);
+ for (i = 0; i < t_args->to_reserve; i++) {
+ hugepages[i] = reserve_hugepage();
+ /* Tests may purposefully try to overallocate, so just
+ * fallthrough rather than error out
+ */
+ if (hugepages[i] == MAP_FAILED) {
+ t_args->to_reserve = i;
+ break;
+ }
+ }
+
+ /* Go to sleep until main thread wakes us up */
+ pthread_mutex_lock(&mutex);
+ while (!wake_cond)
+ pthread_cond_wait(&cond, &mutex);
+ pthread_mutex_unlock(&mutex);
+
+ /* Try to free those hugetlb pages */
+ for (i = 0; i < t_args->to_reserve; i++) {
+ if (munmap(hugepages[i], HPSIZE_BYTES) < 0)
+ ksft_perror("munmap() failed! Check for leaked hugetlb pages!\n");
+ }
+ free(hugepages);
+ return NULL;
+}
+
+/* wake_children
+ *
+ * Helper function to wake children threads.
+ */
+static void wake_children(void)
+{
+ pthread_mutex_lock(&mutex);
+ wake_cond = 1;
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+/* test1
+ *
+ * Sanity checking, attempt to reserve a surplus hugetlb page anywhere.
+ */
+static void test1(void)
+{
+ reset_hugepages();
+
+ write_sysfs(nr_overcommit_hugepages_path, 1);
+ per_thread_args[0].my_nodemask = NULL;
+ per_thread_args[0].to_reserve = 1;
+
+ pthread_create(&threads[0], NULL, thread_work, &per_thread_args[0]);
+
+ usleep(500000);
+
+ get_hugepage_stats();
+ ksft_test_result((g_free_hugepages == 1 && g_nr_hugepages == 1 &&
+ g_resv_hugepages == 1 && g_surplus_hugepages == 1) &&
+ ((n0_free_hugepages == 1 && n0_nr_hugepages == 1 &&
+ n0_surplus_hugepages == 1 && n1_free_hugepages == 0 &&
+ n1_nr_hugepages == 0 && n1_surplus_hugepages == 0) ||
+ (n0_free_hugepages == 0 && n0_nr_hugepages == 0 &&
+ n0_surplus_hugepages == 0 && n1_free_hugepages == 1 &&
+ n1_nr_hugepages == 1 && n1_surplus_hugepages == 1)),
+ "Reserve 1 surplus hugepage anywhere\n");
+
+ wake_children();
+ pthread_join(threads[0], NULL);
+ wake_cond = 0;
+ reset_hugepages();
+}
+
+/* test2
+ *
+ * Sanity checking, attempt to reserve a surplus hugetlb page with
+ * a mempolicy.
+ */
+static void test2(void)
+{
+ reset_hugepages();
+
+ write_sysfs(nr_overcommit_hugepages_path, 1);
+ per_thread_args[0].my_nodemask = nodemasks[0];
+ per_thread_args[0].to_reserve = 1;
+
+ pthread_create(&threads[0], NULL, thread_work, &per_thread_args[0]);
+
+ usleep(500000);
+
+ get_hugepage_stats();
+ ksft_test_result(g_free_hugepages == 1 && g_nr_hugepages == 1 &&
+ g_resv_hugepages == 1 && g_surplus_hugepages == 1 &&
+ n0_free_hugepages == 1 && n0_nr_hugepages == 1 &&
+ n0_surplus_hugepages == 1 && n1_free_hugepages == 0 &&
+ n1_nr_hugepages == 0 && n1_surplus_hugepages == 0,
+ "Reserve 1 surplus hugepage on node0\n");
+
+ wake_children();
+ pthread_join(threads[0], NULL);
+ wake_cond = 0;
+ reset_hugepages();
+}
+
+/* test3
+ *
+ * Set a static hugepage and reserve off node
+ */
+static void test3(void)
+{
+ reset_hugepages();
+
+ write_sysfs(nr_overcommit_hugepages_path, 1);
+ write_sysfs(n0_nr_hugepages_path, 1);
+
+ per_thread_args[0].my_nodemask = nodemasks[0];
+ per_thread_args[0].to_reserve = 0;
+ per_thread_args[1].my_nodemask = nodemasks[1];
+ per_thread_args[1].to_reserve = 1;
+
+ pthread_create(&threads[0], NULL, thread_work, &per_thread_args[0]);
+ pthread_create(&threads[1], NULL, thread_work, &per_thread_args[1]);
+
+ usleep(500000);
+
+ get_hugepage_stats();
+ ksft_test_result(g_free_hugepages == 2 && g_nr_hugepages == 2 &&
+ g_resv_hugepages == 1 && g_surplus_hugepages == 1 &&
+ n0_free_hugepages == 1 && n0_nr_hugepages == 1 &&
+ n0_surplus_hugepages == 0 && n1_free_hugepages == 1 &&
+ n1_nr_hugepages == 1 && n1_surplus_hugepages == 1,
+ "Set 1 static hugepage on node0, reserve surplus hugepage on node 1\n");
+
+ wake_children();
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+ wake_cond = 0;
+ reset_hugepages();
+}
+
+/* test4
+ *
+ * Reserve static hugepage on node0, reserve surplus hugepage on node1
+ */
+static void test4(void)
+{
+ reset_hugepages();
+
+ write_sysfs(nr_overcommit_hugepages_path, 1);
+ write_sysfs(n0_nr_hugepages_path, 1);
+
+ per_thread_args[0].my_nodemask = nodemasks[0];
+ per_thread_args[0].to_reserve = 1;
+ per_thread_args[1].my_nodemask = nodemasks[1];
+ per_thread_args[1].to_reserve = 1;
+
+ pthread_create(&threads[0], NULL, thread_work, &per_thread_args[0]);
+ pthread_create(&threads[1], NULL, thread_work, &per_thread_args[1]);
+
+ usleep(500000);
+
+ get_hugepage_stats();
+ ksft_test_result(g_free_hugepages == 2 && g_nr_hugepages == 2 &&
+ g_resv_hugepages == 2 && g_surplus_hugepages == 1 &&
+ n0_free_hugepages == 1 && n0_nr_hugepages == 1 &&
+ n0_surplus_hugepages == 0 && n1_free_hugepages == 1 &&
+ n1_nr_hugepages == 1 && n1_surplus_hugepages == 1,
+ "Reserve 1 static hugepage on node0, reserve surplus hugepage on node 1\n");
+
+ wake_children();
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+ wake_cond = 0;
+ reset_hugepages();
+}
+
+/* test5
+ *
+ * Reserve static hugepage on node0, reserve surplus hugepage on node1 and
+ * fail to over allocate another.
+ */
+static void test5(void)
+{
+ reset_hugepages();
+
+ write_sysfs(nr_overcommit_hugepages_path, 1);
+ write_sysfs(n0_nr_hugepages_path, 1);
+
+ per_thread_args[0].my_nodemask = nodemasks[0];
+ per_thread_args[0].to_reserve = 1;
+ per_thread_args[1].my_nodemask = nodemasks[1];
+ per_thread_args[1].to_reserve = 2;
+
+ pthread_create(&threads[0], NULL, thread_work, &per_thread_args[0]);
+ pthread_create(&threads[1], NULL, thread_work, &per_thread_args[1]);
+
+ usleep(500000);
+
+ get_hugepage_stats();
+ ksft_test_result(g_free_hugepages == 2 && g_nr_hugepages == 2 &&
+ g_resv_hugepages == 2 && g_surplus_hugepages == 1 &&
+ n0_free_hugepages == 1 && n0_nr_hugepages == 1 &&
+ n0_surplus_hugepages == 0 && n1_free_hugepages == 1 &&
+ n1_nr_hugepages == 1 && n1_surplus_hugepages == 1,
+ "Intentionally overallocate and fail due to nr_overcommit_hugepages limit.\n");
+
+ wake_children();
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+ wake_cond = 0;
+ reset_hugepages();
+
+}
+
+int main(void)
+{
+ ksft_print_header();
+ ksft_set_plan(5);
+
+ check_requirements();
+ setup_threads();
+ setup_node_info();
+ setup_paths();
+ save_hugepage_configs();
+ wake_cond = 0;
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+
+ restore_hugepage_configs();
+ ksft_finished();
+}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index c17b133a81d248..cd368ce590a74c 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -297,6 +297,7 @@ CATEGORY="hugetlb" run_test ./hugepage-mremap
CATEGORY="hugetlb" run_test ./hugepage-vmemmap
CATEGORY="hugetlb" run_test ./hugetlb-madvise
CATEGORY="hugetlb" run_test ./hugetlb_dio
+CATEGORY="hugetlb" run_test ./hugetlb_surplus_mempolicy
if [ "${HAVE_HUGEPAGES}" = "1" ]; then
nr_hugepages_tmp=$(cat /proc/sys/vm/nr_hugepages)
|
| 3/2 | 2026/07/08 05:06 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9164 output: 20 thoughts: 372 Thoughts: **Analyzing the HugeTLB Surplus Page Gathering Patch** Okay, so I'm looking at this patch and my initial thought is, it seems to be touching a pretty critical part of the hugepages management, specifically within `mm/hugetlb.c`. The core of the change is in how surplus huge pages are gathered, and the key modification involves the `gather_surplus_pages` function. What's immediately interesting is this inclusion of `allowed_mems_nr(h)` in the calculations of `needed`. From the code snippet, it looks like it's now incorporated to determine the surplus. It is specifically changing the calculation logic from: ```c - needed = (h->resv_huge_pages + delta) - h->free_huge_pages; + needed = max_t(long, (delta - allowed_mems_nr(h)), + ((h->resv_huge_pages + delta) - h->free_huge_pages)); ``` and ```c - needed = (h->resv_huge_pages + delta) - - (h->free_huge_pages + allocated); + needed = max_t(long, ((delta - allowed_mems_nr(h)) - allocated), + ((h->resv_huge_pages + delta) - + (h->free_huge_pages + allocated))); ``` My understanding is that `allowed_mems_nr(h)` must be somehow checking the current cpuset and mempolicy to refine the surplus calculation. Makes sense, because that affects how hugepages are allowed to be allocated across memory nodes. I need to dig deeper into the context of `allowed_mems_nr` to verify its function here. Of course, the change in `mm/hugetlb.c` is accompanied by a new test in `tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c`. Good, that means the developers are doing the right thing and testing this new behavior. Given this is in `mm/hugetlb.c`, and it's dealing with surplus pages, I'd expect `CONFIG_HUGETLB_PAGE` to be the primary configuration item. I should verify that that's true, just to be sure, but it seems very likely. Let's see if this patch makes sure `gather_surplus_pages` is reachable, and ensure the right configs are set. |
| 4/2 | 2026/07/08 05:06 | tool | grepper |
0mArgs:{
"Expression": "gather_surplus_pages"
}Results: {
"Output": "mm/hugetlb.c=2258=static unsigned int allowed_mems_nr(struct hstate *h)\n--\nmm/hugetlb.c-2278- */\nmm/hugetlb.c:2279:static int gather_surplus_pages(struct hstate *h, long delta)\nmm/hugetlb.c-2280-\t__must_hold(\u0026hugetlb_lock)\n--\nmm/hugetlb.c=4614=static int hugetlb_acct_memory(struct hstate *h, long delta)\n--\nmm/hugetlb.c-4645-\tif (delta \u003e 0) {\nmm/hugetlb.c:4646:\t\tif (gather_surplus_pages(h, delta) \u003c 0)\nmm/hugetlb.c-4647-\t\t\tgoto out;\n"
} |
| 5/2 | 2026/07/08 05:06 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10323 output: 22 thoughts: 38 Thoughts: Here's my analysis of the provided thought process: **Tracing the Call Chain** Okay, so I see `gather_surplus_pages` is invoked by `hugetlb_acct_memory`. That's a key piece of information. Now, to understand the flow and potential impact, I need to know the bigger picture. My next logical step is to pinpoint *where* `hugetlb_acct_memory` is being called from. Understanding the call stack upwards gives me context. Knowing the caller of `hugetlb_acct_memory` is crucial to evaluating the implications for memory accounting within the hugetlb system. Let's dig in and find out. |
| 6/2 | 2026/07/08 05:06 | tool | grepper |
0mArgs:{
"Expression": "hugetlb_acct_memory"
}Results: {
"Output": "Documentation/mm/hugetlbfs_reserv.rst=149=After consulting the reservation map and subpool, the number of needed new\nDocumentation/mm/hugetlbfs_reserv.rst:150:reservations is known. The routine hugetlb_acct_memory() is called to check\nDocumentation/mm/hugetlbfs_reserv.rst:151:for and take the requested number of reservations. hugetlb_acct_memory()\nDocumentation/mm/hugetlbfs_reserv.rst-152-calls into routines that potentially allocate and adjust surplus page counts.\n--\nDocumentation/mm/hugetlbfs_reserv.rst=310=the min_hpages field of a struct hugepage_subpool. At mount time,\nDocumentation/mm/hugetlbfs_reserv.rst:311:hugetlb_acct_memory(min_hpages) is called to reserve the specified number of\nDocumentation/mm/hugetlbfs_reserv.rst-312-huge pages. If they can not be reserved, the mount fails.\n--\nDocumentation/mm/hugetlbfs_reserv.rst=558=into account. While cpusets are not exactly the same as memory policy, this\nDocumentation/mm/hugetlbfs_reserv.rst:559:comment in hugetlb_acct_memory sums up the interaction between reservations\nDocumentation/mm/hugetlbfs_reserv.rst-560-and cpusets/memory policy::\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst=116=HPAGE_RESV_OWNER标志被设置,以表明该VMA拥有预留。\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-125-\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst:126:在咨询了预留映射和子池之后,就知道了需要的新预留数量。hugetlb_acct_memory()函数被调用以检查\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst:127:并获取所要求的预留数量。hugetlb_acct_memory()调用到可能分配和调整剩余页数的函数。然而,在这\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-128-些函数中,代码只是检查以确保有足够的空闲的巨页来容纳预留。如果有的话,全局预留计数resv_huge_pages\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst=226=page-\u003eprivate字段指向与该页相关的任何子池。如果PagePrivate标志被设置,它表明全局预留计数\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-244-如果指定了这个选项,与min_size相对应的巨页的数量将被预留给文件系统使用。这个数字在结构体\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst:245:hugepage_subpool的min_hpages字段中被跟踪。在挂载时,hugetlb_acct_memory(min_hpages)\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-246-被调用以预留指定数量的巨页。如果它们不能被预留,挂载就会失败。\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst=331=region_count()在解除私有巨页映射时被调用。在私有映射中,预留映射中没有条目表明存在一个预留。\n--\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-410-在一段时间后加入的。当预留被添加时,没有尝试将内存策略考虑在内。虽然cpusets与内存策略不\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst:411:完全相同,但hugetlb_acct_memory中的这个注释总结了预留和cpusets/内存策略之间的相互作\nDocumentation/translations/zh_CN/mm/hugetlbfs_reserv.rst-412-用::\n--\nmm/hugetlb.c=114=struct mutex *hugetlb_fault_mutex_table __ro_after_init;\n--\nmm/hugetlb.c-116-/* Forward declaration */\nmm/hugetlb.c:117:static int hugetlb_acct_memory(struct hstate *h, long delta);\nmm/hugetlb.c-118-static void hugetlb_vma_lock_free(struct vm_area_struct *vma);\n--\nmm/hugetlb.c=136=static inline void unlock_or_release_subpool(struct hugepage_subpool *spool,\n--\nmm/hugetlb.c-145-\t\tif (spool-\u003emin_hpages != -1)\nmm/hugetlb.c:146:\t\t\thugetlb_acct_memory(spool-\u003ehstate,\nmm/hugetlb.c-147-\t\t\t\t\t\t-spool-\u003emin_hpages);\n--\nmm/hugetlb.c=152=struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,\n--\nmm/hugetlb.c-166-\nmm/hugetlb.c:167:\tif (min_hpages != -1 \u0026\u0026 hugetlb_acct_memory(h, min_hpages)) {\nmm/hugetlb.c-168-\t\tkfree(spool);\n--\nmm/hugetlb.c=958=void hugetlb_fix_reserve_counts(struct inode *inode)\n--\nmm/hugetlb.c-967-\nmm/hugetlb.c:968:\t\tif (!hugetlb_acct_memory(h, 1))\nmm/hugetlb.c-969-\t\t\treserved = true;\n--\nmm/hugetlb.c=2891=struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,\n--\nmm/hugetlb.c-3016-\t\t\trsv_adjust = hugepage_subpool_put_pages(spool, 1);\nmm/hugetlb.c:3017:\t\t\thugetlb_acct_memory(h, -rsv_adjust);\nmm/hugetlb.c-3018-\t\t\tspin_lock_irq(\u0026hugetlb_lock);\n--\nmm/hugetlb.c-3052-\t\tgbl_reserve = hugepage_subpool_put_pages(spool, 1);\nmm/hugetlb.c:3053:\t\thugetlb_acct_memory(h, -gbl_reserve);\nmm/hugetlb.c-3054-\t}\n--\nmm/hugetlb.c=4604=unsigned long hugetlb_total_pages(void)\n--\nmm/hugetlb.c-4613-\nmm/hugetlb.c:4614:static int hugetlb_acct_memory(struct hstate *h, long delta)\nmm/hugetlb.c-4615-{\n--\nmm/hugetlb.c=4704=static void hugetlb_vm_op_close(struct vm_area_struct *vma)\n--\nmm/hugetlb.c-4728-\t\tgbl_reserve = hugepage_subpool_put_pages(spool, reserve);\nmm/hugetlb.c:4729:\t\thugetlb_acct_memory(h, -gbl_reserve);\nmm/hugetlb.c-4730-\t}\n--\nmm/hugetlb.c=6556=long hugetlb_reserve_pages(struct inode *inode,\n--\nmm/hugetlb.c-6644-\t */\nmm/hugetlb.c:6645:\terr = hugetlb_acct_memory(h, gbl_reserve);\nmm/hugetlb.c-6646-\tif (err \u003c 0)\n--\nmm/hugetlb.c-6663-\t\tif (unlikely(add \u003c 0)) {\nmm/hugetlb.c:6664:\t\t\thugetlb_acct_memory(h, -gbl_reserve);\nmm/hugetlb.c-6665-\t\t\terr = add;\n--\nmm/hugetlb.c-6686-\t\t\t\t\t\t\t\tchg - add);\nmm/hugetlb.c:6687:\t\t\thugetlb_acct_memory(h, -rsv_adjust);\nmm/hugetlb.c-6688-\t\t} else if (h_cg) {\n--\nmm/hugetlb.c-6708-\t\t */\nmm/hugetlb.c:6709:\t\thugetlb_acct_memory(h, -gbl_resv);\nmm/hugetlb.c-6710-\t}\n--\nmm/hugetlb.c=6737=long hugetlb_unreserve_pages(struct inode *inode, long start, long end,\n--\nmm/hugetlb.c-6772-\tgbl_reserve = hugepage_subpool_put_pages(spool, (chg - freed));\nmm/hugetlb.c:6773:\thugetlb_acct_memory(h, -gbl_reserve);\nmm/hugetlb.c-6774-\n"
} |
| 7/2 | 2026/07/08 05:06 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12878 output: 20 thoughts: 164 Thoughts: **Analyzing HugeTLB Memory Accounting** Okay, so I'm diving into this `hugetlb_acct_memory` function, and it's popping up all over the place. Seems like it's a critical component, especially when we're mapping those hugetlbfs files using `mmap` with huge pages – the `hugetlb_reserve_pages` function is a key entry point there. Specifically, this test, `hugetlb_surplus_mempolicy`, it's doing an anonymous, private mapping with `MAP_HUGETLB`, which, as I predicted, will call `hugetlb_reserve_pages`. That in turn calls `hugetlb_acct_memory` which will call `gather_surplus_pages`. Excellent. This confirms my hypothesis. I suspected that the code path was reachable, and now I've confirmed it. Now, let's turn our attention to `allowed_mems_nr` within `mm/hugetlb.c`. That's where I need to focus my attention. |
| 8/2 | 2026/07/08 05:06 | tool | grepper |
0mArgs:{
"Expression": "allowed_mems_nr"
}Results: {
"Output": "mm/hugetlb.c=2241=static nodemask_t *policy_mbind_nodemask(gfp_t gfp)\n--\nmm/hugetlb.c-2257-\nmm/hugetlb.c:2258:static unsigned int allowed_mems_nr(struct hstate *h)\nmm/hugetlb.c-2259-{\n--\nmm/hugetlb.c=2279=static int gather_surplus_pages(struct hstate *h, long delta)\n--\nmm/hugetlb.c-2296-\tlockdep_assert_held(\u0026hugetlb_lock);\nmm/hugetlb.c:2297:\tneeded = max_t(long, (delta - allowed_mems_nr(h)),\nmm/hugetlb.c-2298-\t\t\t((h-\u003eresv_huge_pages + delta) - h-\u003efree_huge_pages));\n--\nmm/hugetlb.c-2331-\tspin_lock_irq(\u0026hugetlb_lock);\nmm/hugetlb.c:2332:\tneeded = max_t(long, ((delta - allowed_mems_nr(h)) - allocated),\nmm/hugetlb.c-2333-\t\t\t((h-\u003eresv_huge_pages + delta) -\n--\nmm/hugetlb.c=4614=static int hugetlb_acct_memory(struct hstate *h, long delta)\n--\nmm/hugetlb.c-4648-\nmm/hugetlb.c:4649:\t\tif (delta \u003e allowed_mems_nr(h)) {\nmm/hugetlb.c-4650-\t\t\treturn_unused_surplus_pages(h, delta);\n"
} |
| 9/2 | 2026/07/08 05:06 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13739 output: 23 thoughts: 176 Thoughts: **Fuzzing Potential: A Deep Dive into HugeTLB Surplus Pages** Okay, so this patch is interesting. It's moving `allowed_mems_nr` further up the file, making it accessible to `gather_surplus_pages`. A seemingly minor structural change, but it's the modification within `gather_surplus_pages` that I really need to analyze. The code's logic is being updated to factor in `allowed_mems_nr(h)` when determining the value of `needed`. Is this fuzz-worthy? Absolutely. This is a functional change within the kernel's hugetlb subsystem, touching core memory management. My initial focus has to be on `gather_surplus_pages` and `allowed_mems_nr`. Those are the critical symbols here. From a configuration perspective, I'm thinking `HUGETLB_PAGE` and `NUMA` are going to be key, and I should check for any others. Right, `allowed_mems_nr` utilizes `cpuset_current_mems_allowed`. That's pointing me directly toward `CPUSETS`. I need to understand the relationship between these subsystems. I have to look into the implementation of `allowed_mems_nr` immediately. |
| 10/2 | 2026/07/08 05:06 | tool | grepper |
0mArgs:{
"Expression": "cpuset_current_mems_allowed"
}Results: {
"Output": "include/linux/cpuset.h=83=extern nodemask_t cpuset_mems_allowed(struct task_struct *p);\ninclude/linux/cpuset.h:84:#define cpuset_current_mems_allowed (current-\u003emems_allowed)\ninclude/linux/cpuset.h-85-void cpuset_init_current_mems_allowed(void);\n--\ninclude/linux/cpuset.h=219=static inline nodemask_t cpuset_mems_allowed(struct task_struct *p)\n--\ninclude/linux/cpuset.h-223-\ninclude/linux/cpuset.h:224:#define cpuset_current_mems_allowed (node_states[N_MEMORY])\ninclude/linux/cpuset.h-225-static inline void cpuset_init_current_mems_allowed(void) {}\n--\nkernel/sched/fair.c=3484=static void task_numa_work(struct callback_head *work)\n--\nkernel/sched/fair.c-3515-\t */\nkernel/sched/fair.c:3516:\tif (cpusets_enabled() \u0026\u0026 nodes_weight(cpuset_current_mems_allowed) == 1) {\nkernel/sched/fair.c:3517:\t\ttrace_sched_skip_cpuset_numa(current, \u0026cpuset_current_mems_allowed);\nkernel/sched/fair.c-3518-\t\treturn;\n--\nmm/hugetlb.c=2258=static unsigned int allowed_mems_nr(struct hstate *h)\n--\nmm/hugetlb.c-2266-\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\nmm/hugetlb.c:2267:\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\nmm/hugetlb.c-2268-\t\tif (!mbind_nodemask || node_isset(node, *mbind_nodemask))\n--\nmm/hugetlb.c=2279=static int gather_surplus_pages(struct hstate *h, long delta)\n--\nmm/hugetlb.c-2291-\tif (mbind_nodemask)\nmm/hugetlb.c:2292:\t\tnodes_and(alloc_nodemask, *mbind_nodemask, cpuset_current_mems_allowed);\nmm/hugetlb.c-2293-\telse\nmm/hugetlb.c:2294:\t\talloc_nodemask = cpuset_current_mems_allowed;\nmm/hugetlb.c-2295-\n--\nmm/mempolicy.c=404=static int mpol_set_nodemask(struct mempolicy *pol,\n--\nmm/mempolicy.c-418-\tnodes_and(nsc-\u003emask1,\nmm/mempolicy.c:419:\t\t cpuset_current_mems_allowed, node_states[N_MEMORY]);\nmm/mempolicy.c-420-\n--\nmm/mempolicy.c-430-\telse\nmm/mempolicy.c:431:\t\tpol-\u003ew.cpuset_mems_allowed = cpuset_current_mems_allowed;\nmm/mempolicy.c-432-\n--\nmm/mempolicy.c=1147=static long do_get_mempolicy(int *policy, nodemask_t *nmask,\n--\nmm/mempolicy.c-1163-\t\ttask_lock(current);\nmm/mempolicy.c:1164:\t\t*nmask = cpuset_current_mems_allowed;\nmm/mempolicy.c-1165-\t\ttask_unlock(current);\n--\nmm/oom_kill.c=249=static enum oom_constraint constrained_alloc(struct oom_control *oc)\n--\nmm/oom_kill.c-298-\t\toc-\u003etotalpages = total_swap_pages;\nmm/oom_kill.c:299:\t\tfor_each_node_mask(nid, cpuset_current_mems_allowed)\nmm/oom_kill.c-300-\t\t\toc-\u003etotalpages += node_present_pages(nid);\n--\nmm/page_alloc.c=4687=__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,\n--\nmm/page_alloc.c-4767-\t\t\t\t\tac-\u003ehighest_zoneidx,\nmm/page_alloc.c:4768:\t\t\t\t\t\u0026cpuset_current_mems_allowed);\nmm/page_alloc.c-4769-\t\tif (!zonelist_zone(z))\n--\nmm/page_alloc.c=4973=static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,\n--\nmm/page_alloc.c-4989-\t\tif (in_task() \u0026\u0026 !ac-\u003enodemask)\nmm/page_alloc.c:4990:\t\t\tac-\u003enodemask = \u0026cpuset_current_mems_allowed;\nmm/page_alloc.c-4991-\t\telse\n--\nmm/page_alloc.c=5190=struct page *__alloc_frozen_pages_noprof(gfp_t gfp, unsigned int order,\n--\nmm/page_alloc.c-5234-\t * Restore the original nodemask if it was potentially replaced with\nmm/page_alloc.c:5235:\t * \u0026cpuset_current_mems_allowed to optimize the fast-path attempt.\nmm/page_alloc.c-5236-\t */\n--\nmm/show_mem.c=119=static bool show_mem_node_skip(unsigned int flags, int nid, nodemask_t *nodemask)\n--\nmm/show_mem.c-129-\tif (!nodemask)\nmm/show_mem.c:130:\t\tnodemask = \u0026cpuset_current_mems_allowed;\nmm/show_mem.c-131-\n"
} |
| 11/2 | 2026/07/08 05:06 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15737 output: 20 thoughts: 36 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating `HUGETLB_PAGE` and `NUMA` in the Kernel Configuration** Alright, so the task at hand involves examining `HUGETLB_PAGE` and `NUMA` within the kernel configuration system. Let's dig in. My immediate thought is, "What's the relationship, if any, between these two?" Perhaps they're interdependent, or maybe they're orthogonal features that interact in interesting ways. Since the prompt specifically calls them out, I'm already mentally running through potential implications: huge pages for memory allocation, NUMA for managing memory access latency. I'll need to check the Kconfig files to see how these options are defined, their dependencies, and any interactions they specify. This could tell a great story about optimization and how the kernel handles resource management, and this kind of analysis is right in my wheelhouse. Time to fire up the tools! |
| 12/2 | 2026/07/08 05:06 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLB_PAGE"
}Results: {
"Output": "arch/sh/mm/Kconfig=158=choice\n--\narch/sh/mm/Kconfig-163-\narch/sh/mm/Kconfig:164:config HUGETLB_PAGE_SIZE_64K\narch/sh/mm/Kconfig-165-\tbool \"64kB\"\n--\narch/sh/mm/Kconfig-167-\narch/sh/mm/Kconfig:168:config HUGETLB_PAGE_SIZE_256K\narch/sh/mm/Kconfig-169-\tbool \"256kB\"\n--\narch/sh/mm/Kconfig-171-\narch/sh/mm/Kconfig:172:config HUGETLB_PAGE_SIZE_1MB\narch/sh/mm/Kconfig-173-\tbool \"1MB\"\narch/sh/mm/Kconfig-174-\narch/sh/mm/Kconfig:175:config HUGETLB_PAGE_SIZE_4MB\narch/sh/mm/Kconfig-176-\tbool \"4MB\"\n--\narch/sh/mm/Kconfig-178-\narch/sh/mm/Kconfig:179:config HUGETLB_PAGE_SIZE_64MB\narch/sh/mm/Kconfig-180-\tbool \"64MB\"\n--\nfs/Kconfig=262=if HUGETLBFS\nfs/Kconfig:263:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON\nfs/Kconfig-264-\tbool \"HugeTLB Vmemmap Optimization (HVO) defaults to on\"\n--\nfs/Kconfig=271=endif # HUGETLBFS\nfs/Kconfig-272-\nfs/Kconfig:273:config HUGETLB_PAGE\nfs/Kconfig-274-\tdef_bool HUGETLBFS\n--\nfs/Kconfig-276-\nfs/Kconfig:277:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-278-\tdef_bool HUGETLB_PAGE\n--\nmm/Kconfig=653=config ARCH_ENABLE_THP_MIGRATION\n--\nmm/Kconfig-655-\nmm/Kconfig:656:config HUGETLB_PAGE_SIZE_VARIABLE\nmm/Kconfig-657-\tdef_bool n\n"
} |
| 13/2 | 2026/07/08 05:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16597 output: 19 thoughts: 44 Thoughts: **Checking the Foundation: HugeTLB and HugeTLBFS** Okay, so `HUGETLB_PAGE` is the key. That definition is pretty straightforward – it's directly tied to `HUGETLBFS`. So, if I need to work with huge pages, the configuration option I'm really after is `HUGETLBFS`. That's the one I need to get my hands on. Time to dive into the configuration and see what `HUGETLBFS` is all about. Let's see what we're working with here. |
| 14/2 | 2026/07/08 05:07 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLBFS"
}Results: {
"Output": "fs/Kconfig=247=config ARCH_SUPPORTS_HUGETLBFS\n--\nfs/Kconfig-249-\nfs/Kconfig:250:menuconfig HUGETLBFS\nfs/Kconfig-251-\tbool \"HugeTLB file system support\"\n"
} |
| 15/2 | 2026/07/08 05:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16857 output: 17 thoughts: 50 Thoughts: Okay, here's my take on those configurations: **Analyzing the Relevant Configurations** Right, so we're talking about memory management and CPU affinity here. The key players jumping out are `HUGETLBFS` and `NUMA`, obviously. But hold on, we can't forget `CPUSETS`. I need to remember that `cpuset_current_mems_allowed` is relevant within this context. Time to double-check `CPUSETS` and make sure it's behaving as expected within the overall setup. I'll need to explore how it's interacting with NUMA and huge pages. |
| 16/2 | 2026/07/08 05:07 | tool | grepper |
0mArgs:{
"Expression": "config CPUSETS"
}Results: {
"Output": "init/Kconfig=1270=config CGROUP_HUGETLB\n--\ninit/Kconfig-1285-\ninit/Kconfig:1286:config CPUSETS\ninit/Kconfig-1287-\tbool \"Cpuset controller\"\n--\ninit/Kconfig-1298-\ninit/Kconfig:1299:config CPUSETS_V1\ninit/Kconfig-1300-\tbool \"Legacy cgroup v1 cpusets controller\"\n"
} |
| 17/2 | 2026/07/08 05:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17186 output: 123 thoughts: 333 Thoughts: **Analyzing the Hugetlb Surplus Memory Policy Test** Okay, so I'm looking at this patch and the associated test, specifically focusing on `HUGETLBFS`, `NUMA`, and `CPUSETS`. I remember now that `NUMA` is a prerequisite for `cpuset_current_mems_allowed` to even make sense, and the test code in `tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c` explicitly uses `numa_available()`. It's clearly using functions like `numa_bind` and `numa_bitmask_setbit`, so `NUMA` is definitely in play here. I need to confirm my understanding by double-checking `gather_surplus_pages` and `allowed_mems_nr` in `mm/hugetlb.c`. Right, the patch is modifying `gather_surplus_pages` to utilize `allowed_mems_nr(h)`. The test is precisely designed to validate this change in behavior. The reasoning here is solid: The patch is touching core hugetlb logic for gathering surplus huge pages, and it's making sure it respects mempolicies and cpusets. It's adding this new selftest to verify this behavior. This is crucial for fuzzing to ensure that there are no regressions or memory leaks when we allocate surplus huge pages under various mempolicies. So, the key symbols here are `gather_surplus_pages` and `allowed_mems_nr`. And the essential configs we need to have enabled are `HUGETLBFS`, `NUMA`, and `CPUSETS`. Let's quickly check to make sure `gather_surplus_pages` is a static function. Yes, it is. And I think `allowed_mems_nr` is also static? Yes, it is. Okay, I've got a clear picture of what's going on. Time to call set-results. |
| 18/2 | 2026/07/08 05:07 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"HUGETLBFS",
"NUMA",
"CPUSETS"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr"
],
"Reasoning": "The patch modifies the core hugetlb logic for gathering surplus huge pages to respect mempolicies and cpusets. It adds a new selftest to verify this behavior. Fuzzing this is important to ensure no regressions or memory leaks occur when allocating surplus huge pages under different mempolicies.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA",
"CPUSETS"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr"
],
"Reasoning": "The patch modifies the core hugetlb logic for gathering surplus huge pages to respect mempolicies and cpusets. It adds a new selftest to verify this behavior. Fuzzing this is important to ensure no regressions or memory leaks occur when allocating surplus huge pages under different mempolicies.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|