| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/06/23 19:15 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr",
"hugetlb_acct_memory",
"policy_mbind_nodemask"
],
"Reasoning": "The patch modifies the core hugetlb logic in `gather_surplus_pages` to take into account the allowed memory nodes (`allowed_mems_nr`) when calculating the needed surplus pages. This is a functional change in memory management that affects how huge pages are reserved and allocated, especially in the presence of NUMA memory policies. Fuzzing should focus on the modified surplus page gathering and accounting logic.",
"WorthFuzzing": true
} |
| 1/1 | 2026/06/23 19:15 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 5021cec86cca1d111a2104fe381d70ab483007ef\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Jun 23 19:15:21 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex f24bf49be047e3..bd97f0f43415e7 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((long) (delta - allowed_mems_nr(h)),\n+\t\t(long) ((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((long) ((delta - allowed_mems_nr(h)) - allocated),\n+\t\t\t(long) ((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..0a77b016935189\n--- /dev/null\n+++ b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c\n@@ -0,0 +1,472 @@\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 = 0;\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 int n1_free_hugepages, n1_nr_hugepages, n1_surplus_hugepages;\n+unsigned long int orig_n0_nr_hugepages, orig_n1_nr_hugepages;\n+unsigned long int 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+\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+\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+\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+\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+\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+ if (geteuid() != 0)\n+ ksft_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+\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+\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(\"setup_node_info: calloc.\");\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+\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+\tper_thread_args = calloc(2, sizeof(per_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+\t}\n+}\n+\n+/* reserve_hugepage\n+ *\n+ * Helper function to reserve a hugetlb page\n+ */\n+static unsigned long* reserve_hugepage(void) {\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+\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 * fall through rather than error out*/\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+\t}\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+\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+\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+\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+\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+\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+\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+\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+\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/06/23 19:15 | agent | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr",
"hugetlb_acct_memory",
"policy_mbind_nodemask"
],
"Reasoning": "The patch modifies the core hugetlb logic in `gather_surplus_pages` to take into account the allowed memory nodes (`allowed_mems_nr`) when calculating the needed surplus pages. This is a functional change in memory management that affects how huge pages are reserved and allocated, especially in the presence of NUMA memory policies. Fuzzing should focus on the modified surplus page gathering and accounting logic.",
"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 5021cec86cca1d111a2104fe381d70ab483007ef
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Jun 23 19:15:21 2026 +0000
syz-cluster: applied patch under review
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f24bf49be047e3..bd97f0f43415e7 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((long) (delta - allowed_mems_nr(h)),
+ (long) ((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((long) ((delta - allowed_mems_nr(h)) - allocated),
+ (long) ((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..0a77b016935189
--- /dev/null
+++ b/tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c
@@ -0,0 +1,472 @@
+// 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 = 0;
+
+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 int n1_free_hugepages, n1_nr_hugepages, n1_surplus_hugepages;
+unsigned long int orig_n0_nr_hugepages, orig_n1_nr_hugepages;
+unsigned long int 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("setup_node_info: calloc.");
+
+ /* 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(per_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
+ * fall through 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();
+
+ 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/06/23 19:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9147 output: 20 thoughts: 377 Thoughts: **Analyzing the HugeTLB Surplus Page Patch** Okay, so I'm looking at a patch that tweaks `mm/hugetlb.c`, specifically the `gather_surplus_pages` function, and includes a new selftest, `hugetlb_surplus_mempolicy.c`. The core of the change seems to be incorporating `allowed_mems_nr(h)` into the calculation of needed huge pages. They've moved its definition higher up in the file, and are now using it within `gather_surplus_pages`. Let's see, the changes in `gather_surplus_pages` itself modify the calculation: ```c - needed = (h->resv_huge_pages + delta) - h->free_huge_pages; + needed = max((long) (delta - allowed_mems_nr(h)), + (long) ((h->resv_huge_pages + delta) - h->free_huge_pages)); ``` and ```c - needed = (h->resv_huge_pages + delta) - - (h->free_huge_pages + allocated); + needed = max((long) ((delta - allowed_mems_nr(h)) - allocated), + (long) ((h->resv_huge_pages + delta) - + (h->free_huge_pages + allocated))); ``` Right, so this logic is fundamentally about how the kernel manages surplus huge pages, and *crucially*, it's now considering the allowed memory nodes (NUMA). Given the functional nature of this change and the involvement of core kernel memory management, this is definitely something that warrants a thorough fuzzing campaign. The interaction with mempolicies, in particular, looks like a good area to focus on during fuzzing. Now, I need to figure out what config options are essential for this to even *be* active. Obviously, `CONFIG_HUGETLB_PAGE` is the primary one, since this is all about HugeTLB pages. Let me just confirm that quickly... Yep, that's the base requirement. Good to know. |
| 4/2 | 2026/06/23 19:15 | 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/06/23 19:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10239 output: 19 thoughts: 40 Thoughts: Here's my analysis of the situation: **Investigating the Interaction of HugeTLB Accounting and Page Retrieval** Okay, so `gather_surplus_pages` is called specifically by `hugetlb_acct_memory`. That's the key starting point. I need to understand how these two functions interact and what configuration parameters might influence their behavior within the `mm/hugetlb.c` context. Given that `gather_surplus_pages` likely handles the retrieval of pages, and `hugetlb_acct_memory` deals with memory accounting within the HugeTLB system, I'm expecting to delve into the potential for memory leaks or inconsistencies. I’ll need to explore how the system determines which pages are considered "surplus," under what conditions this function is triggered, and how the accounting interacts with page reclamation or allocation. The configurations within `mm/hugetlb.c` are where the knobs are likely to be; I'm expecting to find settings related to page size, memory limits, and perhaps policies for page retirement or swapping that impact how these functions work together. I'll need to focus on potential race conditions and performance implications as well. |
| 6/2 | 2026/06/23 19:15 | tool | grepper |
0mArgs:{
"Expression": "hugetlb\\.o"
}Results: {
"Output": "mm/Makefile=80=obj-$(CONFIG_HAS_DMA)\t+= dmapool.o\nmm/Makefile:81:obj-$(CONFIG_HUGETLBFS)\t+= hugetlb.o hugetlb_sysfs.o hugetlb_sysctl.o\nmm/Makefile-82-ifdef CONFIG_CMA\n"
} |
| 7/2 | 2026/06/23 19:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10636 output: 20 thoughts: 60 Thoughts: Okay, let's see, my initial thought is: **Analyzing Possible Dependencies for HugeTLB Configuration**. Right, so I'm focusing on `CONFIG_HUGETLBFS` for this configuration. Seems straightforward enough. But, I also need to consider the interplay with other potentially related configurations. Specifically, `CONFIG_NUMA` catches my eye. The mentions of `allowed_mems_nr` and `policy_mbind_nodemask` within the NUMA context are making me think this might be more interconnected than I initially anticipated. These flags often involve the memory node and binding policies, a critical area when dealing with HugeTLB. Therefore, the next logical step is to dive deeper into `allowed_mems_nr`. That seems like the right place to start understanding the full picture of the possible dependencies here. |
| 8/2 | 2026/06/23 19:15 | 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((long) (delta - allowed_mems_nr(h)),\nmm/hugetlb.c-2298-\t\t(long) ((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((long) ((delta - allowed_mems_nr(h)) - allocated),\nmm/hugetlb.c-2333-\t\t\t(long) ((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/06/23 19:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11342 output: 22 thoughts: 24 Thoughts: Okay, here's my summary, taking the perspective of someone knowledgeable in the field: **Checking for NUMA Policy Bindings** Alright, let's get down to brass tacks. I need to verify something critical related to NUMA and its memory binding capabilities. Specifically, I need to confirm whether `policy_mbind_nodemask` is actually defined. This is a crucial piece of the puzzle for managing memory affinity and placement on systems with Non-Uniform Memory Access architecture. Understanding this definition, or lack thereof, significantly impacts how I approach memory allocation strategies and potentially how I configure the kernel for performance optimization. It's a fundamental check before I can proceed with further analysis or implementation of NUMA-aware code. I want to make sure I am doing it right. |
| 10/2 | 2026/06/23 19:15 | tool | grepper |
0mArgs:{
"Expression": "policy_mbind_nodemask"
}Results: {
"Output": "mm/hugetlb.c=2218=struct folio *alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,\n--\nmm/hugetlb.c-2240-\nmm/hugetlb.c:2241:static nodemask_t *policy_mbind_nodemask(gfp_t gfp)\nmm/hugetlb.c-2242-{\n--\nmm/hugetlb.c=2258=static unsigned int allowed_mems_nr(struct hstate *h)\n--\nmm/hugetlb.c-2265-\nmm/hugetlb.c:2266:\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\nmm/hugetlb.c-2267-\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\n--\nmm/hugetlb.c=2279=static int gather_surplus_pages(struct hstate *h, long delta)\n--\nmm/hugetlb.c-2289-\nmm/hugetlb.c:2290:\tmbind_nodemask = policy_mbind_nodemask(htlb_alloc_mask(h));\nmm/hugetlb.c-2291-\tif (mbind_nodemask)\n"
} |
| 11/2 | 2026/06/23 19:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11904 output: 36 thoughts: 24 Thoughts: Okay, let's break this down. Here's how I'd approach this task, as if these were my internal thoughts: **Checking for NUMA Configuration in Policy Bindings** Alright, I need to investigate the use of `policy_mbind_nodemask` and how it relates to NUMA. First, my immediate instinct is to confirm whether the compilation of this function is conditional on NUMA being enabled. I need to quickly establish if `#ifdef CONFIG_NUMA` controls its availability. That's the logical starting point. I suspect it's probably guarded by that macro. If so, that immediately tells me something about the core system requirements for this feature. If not, I will need to delve further to understand its usage. |
| 12/2 | 2026/06/23 19:16 | tool | read-file |
0mArgs:{
"File": "mm/hugetlb.c",
"FirstLine": 2230,
"LineCount": 50
}Results: {
"Contents": "2230:\t\t\t}\n2231:\t\t}\n2232:\t\tspin_unlock_irq(\u0026hugetlb_lock);\n2233:\t\n2234:\t\t/* We cannot fallback to other nodes, as we could break the per-node pool. */\n2235:\t\tif (!allow_alloc_fallback)\n2236:\t\t\tgfp_mask |= __GFP_THISNODE;\n2237:\t\n2238:\t\treturn alloc_migrate_hugetlb_folio(h, gfp_mask, preferred_nid, nmask);\n2239:\t}\n2240:\t\n2241:\tstatic nodemask_t *policy_mbind_nodemask(gfp_t gfp)\n2242:\t{\n2243:\t#ifdef CONFIG_NUMA\n2244:\t\tstruct mempolicy *mpol = get_task_policy(current);\n2245:\t\n2246:\t\t/*\n2247:\t\t * Only enforce MPOL_BIND policy which overlaps with cpuset policy\n2248:\t\t * (from policy_nodemask) specifically for hugetlb case\n2249:\t\t */\n2250:\t\tif (mpol-\u003emode == MPOL_BIND \u0026\u0026\n2251:\t\t\t(apply_policy_zone(mpol, gfp_zone(gfp)) \u0026\u0026\n2252:\t\t\t cpuset_nodemask_valid_mems_allowed(\u0026mpol-\u003enodes)))\n2253:\t\t\treturn \u0026mpol-\u003enodes;\n2254:\t#endif\n2255:\t\treturn NULL;\n2256:\t}\n2257:\t\n2258:\tstatic unsigned int allowed_mems_nr(struct hstate *h)\n2259:\t{\n2260:\t\tint node;\n2261:\t\tunsigned int nr = 0;\n2262:\t\tnodemask_t *mbind_nodemask;\n2263:\t\tunsigned int *array = h-\u003efree_huge_pages_node;\n2264:\t\tgfp_t gfp_mask = htlb_alloc_mask(h);\n2265:\t\n2266:\t\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\n2267:\t\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\n2268:\t\t\tif (!mbind_nodemask || node_isset(node, *mbind_nodemask))\n2269:\t\t\t\tnr += array[node];\n2270:\t\t}\n2271:\t\n2272:\t\treturn nr;\n2273:\t}\n2274:\t\n2275:\t/*\n2276:\t * Increase the hugetlb pool such that it can accommodate a reservation\n2277:\t * of size 'delta'.\n2278:\t */\n2279:\tstatic int gather_surplus_pages(struct hstate *h, long delta)\n"
} |
| 13/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12892 output: 22 thoughts: 52 Thoughts: Okay, let's break this down. **Analyzing Potential Impacts of a Patch** Right, `CONFIG_NUMA` is definitely worth considering; it's practically screaming relevance here. The patch focuses on changes within `gather_surplus_pages`, which, if I recall correctly, is called specifically by `hugetlb_acct_memory`. That's the key. Let's see... the real impact hinges on how `hugetlb_acct_memory` behaves and where that function is used in relation to NUMA. I need to understand if the NUMA configuration directly influences the behavior of `hugetlb_acct_memory` and how the modifications in `gather_surplus_pages` are going to change the accounting procedure under NUMA architectures. It's time to dig into that `hugetlb_acct_memory` function and its call graph. |
| 14/2 | 2026/06/23 19:16 | 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"
} |
| 15/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15504 output: 23 thoughts: 167 Thoughts: **Analyzing the `hugetlb_surplus_mempolicy` Test Configuration** Okay, so I'm diving into this `hugetlb_surplus_mempolicy` test. First, I need to get a handle on where the core functionality resides. I see that the function `hugetlb_acct_memory` is pretty fundamental, called from all over the place – `hugetlb_reserve_pages`, `hugetlb_unreserve_pages`, and even in allocation/deallocation routines like `alloc_hugetlb_folio` and `hugetlb_vm_op_close`. That's a key piece of the puzzle. Now, let's figure out the necessary configurations for this specific test. Since the test leverages `numa.h` and links with `-lnuma`, I'm pretty confident that `CONFIG_NUMA` is essential; no question about that. It's also highly probable that `CONFIG_HUGETLBFS` and `CONFIG_HUGETLB_PAGE` are required to enable HugeTLB support in the first place. I'll need to double check those, though. To be extra thorough, I'll consult the `tools/testing/selftests/mm/Makefile` directly. I can usually glean specific configuration dependencies from there, which is often more definitive than just guessing. That should give me the definitive list of needed configs for this test. |
| 16/2 | 2026/06/23 19:16 | tool | grepper |
0mArgs:{
"Expression": "hugetlb_surplus_mempolicy"
}Results: {
"Output": "tools/testing/selftests/mm/Makefile=108=TEST_GEN_FILES += folio_split_race_test\ntools/testing/selftests/mm/Makefile:109:TEST_GEN_FILES += hugetlb_surplus_mempolicy\ntools/testing/selftests/mm/Makefile-110-\n--\ntools/testing/selftests/mm/Makefile=262=$(OUTPUT)/rmap: LDLIBS += -lnuma\ntools/testing/selftests/mm/Makefile-263-\ntools/testing/selftests/mm/Makefile:264:$(OUTPUT)/hugetlb_surplus_mempolicy: LDLIBS += -lnuma\ntools/testing/selftests/mm/Makefile-265-\n--\ntools/testing/selftests/mm/hugetlb_surplus_mempolicy.c-2-/*\ntools/testing/selftests/mm/hugetlb_surplus_mempolicy.c:3: * hugetlb_surplus_mempolicy\ntools/testing/selftests/mm/hugetlb_surplus_mempolicy.c-4- *\n--\ntools/testing/selftests/mm/run_vmtests.sh=299=CATEGORY=\"hugetlb\" run_test ./hugetlb_dio\ntools/testing/selftests/mm/run_vmtests.sh:300:CATEGORY=\"hugetlb\" run_test ./hugetlb_surplus_mempolicy\ntools/testing/selftests/mm/run_vmtests.sh-301-\n"
} |
| 17/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16382 output: 18 thoughts: 129 Thoughts: **Analyzing the `hugetlb_surplus_mempolicy` Test for Robustness** Okay, so this patch introduces a new test, `hugetlb_surplus_mempolicy`, which is all about how hugetlb surplus pages and mempolicies play together. That's definitely a functional area with the potential for subtle interactions and edge cases, making it a prime candidate for fuzzing. We need to be confident in this area. Looking at the code, the key symbols that immediately jump out and require a closer look are `gather_surplus_pages`, `allowed_mems_nr`, and `hugetlb_acct_memory`. These are likely where the core logic of this new behavior resides. I'll need to pay close attention to how they handle the mempolicy aspects during surplus page management. Now, let's just quickly confirm that the proposed configurations are on the right track. We have `HUGETLBFS`, `HUGETLB_PAGE`, and `NUMA` listed. It makes sense, as all those settings have a clear relationship to the topic, but it's always worth a double-check to make sure we're not missing a critical dependency or accidentally enabling something that shouldn't be involved. |
| 18/2 | 2026/06/23 19:16 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLB"
}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=247=config ARCH_SUPPORTS_HUGETLBFS\n--\nfs/Kconfig-249-\nfs/Kconfig:250:menuconfig HUGETLBFS\nfs/Kconfig-251-\tbool \"HugeTLB file system support\"\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--\nfs/Kconfig-282-\nfs/Kconfig:283:config HUGETLB_PMD_PAGE_TABLE_SHARING\nfs/Kconfig-284-\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"
} |
| 19/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17512 output: 20 thoughts: 70 Thoughts: Here's my take on those thoughts: **Deep Dive into Huge Pages Configuration** Okay, so I'm focusing on huge pages here, and it looks like `HUGETLBFS` is the central configuration parameter. I'm noting that the `HUGETLB_PAGE` option defaults to `HUGETLBFS` if not explicitly set otherwise, which makes sense for the general setup. I need to make sure NUMA is properly enabled as well. To really cement my understanding, I'm going to take a closer look at `gather_surplus_pages` and `allowed_mems_nr` functions within `mm/hugetlb.c`. These are key when you're managing memory allocation and ensuring the appropriate NUMA nodes are considered, especially when dealing with surplus pages. Understanding these functions will clarify how the kernel handles the finer points of huge page allocation and utilization, particularly in resource-constrained environments. I want to make sure I understand the nuances around memory node constraints. |
| 20/2 | 2026/06/23 19:16 | 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"
} |
| 21/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18039 output: 22 thoughts: 172 Thoughts: **My Analysis of this Patch** Okay, so this patch is clearly centered around memory management, specifically dealing with surplus pages, and it's adding a new variable, `allowed_mems_nr`. My initial instinct is to set `WorthFuzzing=true`; anything related to page allocation and accounting warrants close scrutiny. My `FocusSymbols` are now set: `gather_surplus_pages`, `allowed_mems_nr`, and `hugetlb_acct_memory`. Those are the core functions and variables I need to understand thoroughly to get a grasp on the intent and impact of this code. Plus I need to enable some configurations, `HUGETLBFS` and `NUMA`, for the fuzzing. Now, let me confirm the static nature of these symbols. Good, `allowed_mems_nr`, `gather_surplus_pages`, and `hugetlb_acct_memory` are all static. This gives me a slightly better handle on their scope, restricting external modification. I also need to see if there are any other related functions to consider. Aha! `policy_mbind_nodemask` is also static, and it's used by `allowed_mems_nr`. This seems like a promising addition to the set and I need to investigate it further. |
| 22/2 | 2026/06/23 19:16 | tool | grepper |
0mArgs:{
"Expression": "policy_mbind_nodemask"
}Results: {
"Output": "mm/hugetlb.c=2218=struct folio *alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,\n--\nmm/hugetlb.c-2240-\nmm/hugetlb.c:2241:static nodemask_t *policy_mbind_nodemask(gfp_t gfp)\nmm/hugetlb.c-2242-{\n--\nmm/hugetlb.c=2258=static unsigned int allowed_mems_nr(struct hstate *h)\n--\nmm/hugetlb.c-2265-\nmm/hugetlb.c:2266:\tmbind_nodemask = policy_mbind_nodemask(gfp_mask);\nmm/hugetlb.c-2267-\tfor_each_node_mask(node, cpuset_current_mems_allowed) {\n--\nmm/hugetlb.c=2279=static int gather_surplus_pages(struct hstate *h, long delta)\n--\nmm/hugetlb.c-2289-\nmm/hugetlb.c:2290:\tmbind_nodemask = policy_mbind_nodemask(htlb_alloc_mask(h));\nmm/hugetlb.c-2291-\tif (mbind_nodemask)\n"
} |
| 23/2 | 2026/06/23 19:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18865 output: 165 thoughts: 175 Thoughts: **My Analysis of the Proposed Change and Fuzzing Strategy** Okay, so this patch is a definite addition to the landscape of huge page management, specifically within the context of NUMA systems. It alters the core `gather_surplus_pages` function, and that immediately tells me we need to be very focused on it. The key is that the patch now considers the `allowed_mems_nr` parameter when determining how many surplus pages are needed. This is a functional change – a behavioral modification – in how huge pages are both reserved and ultimately allocated. This is a critical point that needs thorough examination. Given the nature of the change, which explicitly involves memory nodes and potentially restrictive NUMA policies, I think it's spot on to add `policy_mbind_nodemask` to the `FocusSymbols`. It will be a key factor in understanding the intricacies of the new logic. The patch is tied to how the system *accounts* for these pages and *gathers* them, which makes these key focus areas, and clearly, `hugetlb_acct_memory` is also critical. These are all core symbols we'll need to examine. We're dealing with `HUGETLBFS` and `NUMA` as the relevant configurations, of course. The fuzzing strategy, therefore, should concentrate on the modifications to the surplus page gathering and accounting logic. I'm imagining scenarios with diverse NUMA configurations, varied memory policies (including those employing `mbind`), and high loads to stress-test the new logic under different conditions. The fuzzing target should include `gather_surplus_pages` and associated functions to ensure we are covering the changes effectively. Also, I will need to be able to evaluate the results of each fuzz run effectively, let's call that `set-results`. I will have to ensure that is set up to record results that are understandable and easily evaluated. |
| 24/2 | 2026/06/23 19:16 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"HUGETLBFS",
"NUMA"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr",
"hugetlb_acct_memory",
"policy_mbind_nodemask"
],
"Reasoning": "The patch modifies the core hugetlb logic in `gather_surplus_pages` to take into account the allowed memory nodes (`allowed_mems_nr`) when calculating the needed surplus pages. This is a functional change in memory management that affects how huge pages are reserved and allocated, especially in the presence of NUMA memory policies. Fuzzing should focus on the modified surplus page gathering and accounting logic.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"HUGETLBFS",
"NUMA"
],
"FocusSymbols": [
"gather_surplus_pages",
"allowed_mems_nr",
"hugetlb_acct_memory",
"policy_mbind_nodemask"
],
"Reasoning": "The patch modifies the core hugetlb logic in `gather_surplus_pages` to take into account the allowed memory nodes (`allowed_mems_nr`) when calculating the needed surplus pages. This is a functional change in memory management that affects how huge pages are reserved and allocated, especially in the presence of NUMA memory policies. Fuzzing should focus on the modified surplus page gathering and accounting logic.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|