From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 398A243DA28 for ; Tue, 7 Jul 2026 18:29:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783448972; cv=none; b=cnLqd0ASHJv4NjXwqCzZp9PKHL12HkVIKiN9dVASPB2YI7wCIL5bt1umPvNDlV2qBIqm//VwnokmwH2yOM2pnkv2lO9z5yZtLKH/UA72RXA+H719JzNtEpy/JyR/2SNei23lXWiCbGmUG079L+P4fhcH2PX1pwtlG1WDCivvAl4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783448972; c=relaxed/simple; bh=R2J7SNm3zaUcNXopA+eDf9QBBDmnsLF7yclvXeJFAAg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ROY0GhLOm0orqE+1qZto8DbqCV9tBjiZIx+C1fVdjCV2x/ta0NHfpZ3qR9U/eLLI6yAn8cXqgBNA4ZT/2ytSBcH+yC1lQbtpr6ryAYzWMA71FZKK+/Von1uj94KuI0Hii3ekhkI1V7M9iNnn81fc//DOEBT/pTXVtQBn0nYAWuw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Vc3X8Wol; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=dyzanSA9; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Vc3X8Wol"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="dyzanSA9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1783448969; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3SkRPUn9X6TAls57Zd+/V7aSZfttFWHFsmLqlEdELyU=; b=Vc3X8WolHDzfbjpCgaCFFEbK0DCFoMb0oFyBxWEbpkedp3Jq4DN3Xd6vsyHA7caprz4iIJ BBvWszk06BUrCrrihBPSjL6nttWWTePo6vq9b06kPXq+Se7ycj21cwrK54RmXNGHwWv3Oy DwbOxfqItttNWZ9bieTIDKEO5w7vO8U= Received: from mail-oi1-f199.google.com (mail-oi1-f199.google.com [209.85.167.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-57-cv8FLVgSNkaA2yiM93XdNw-1; Tue, 07 Jul 2026 14:29:26 -0400 X-MC-Unique: cv8FLVgSNkaA2yiM93XdNw-1 X-Mimecast-MFC-AGG-ID: cv8FLVgSNkaA2yiM93XdNw_1783448965 Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-497e582f42fso6128774b6e.3 for ; Tue, 07 Jul 2026 11:29:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1783448965; x=1784053765; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=3SkRPUn9X6TAls57Zd+/V7aSZfttFWHFsmLqlEdELyU=; b=dyzanSA9L0+0jjE4wr/q6h8XTkCShIH9ndb1m110MPEGsBHERRh7UsQKJBy7dAx1At ihM6NQGFMc3djTTGBZr6e4CjcfBR8D+Ct/0LPoOYMImvIl5S0zeMZE8orPvSJGhSFLB9 IFCvaTGQvdWnhzr5BfhGqhQXGpfGZXZLF1OHwbcZJqcLBCH9kQIKrmzkLbuhi42lHH+n TpHV/lgDthPAaO9kzqmTdw52XCrTplUPXf6B4XIAKnAVK+fgyRNPDGvys2Nzyk5h8a4T BMwD62bfvu+LP+oXaXugB/WTze68aBy/SYsGpAVrVKse8H+ZSf2lHHuNnIEdbfMX+zZv 0uKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783448965; x=1784053765; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=3SkRPUn9X6TAls57Zd+/V7aSZfttFWHFsmLqlEdELyU=; b=s+D9kvcyUh4IlwP7lR/vQ3P9RLeEIqR2LLCB+8f1ByVoohGtqdfXL3hsCqlk4zhwOt KoI53TsexuQTXn9PJLE5TlIHg4xhuNqfYNV851VjKZKVbfGcxvemrqjtRE+xEvd7qASe TYcmIijPxowt43D0a5z0p0AqmlSi7S4CaxkGuMKEiD0HLEK95UpGYbNRh9W22JgiRAtn 5XOnhw0Kqw/iIzGfoWt5zC0ZRKc7wKLki/C3UFQV6278q9wbMWOy9Rfrmwj+hjMG3QS8 6BYNsdH6Kvp/Oo6IPn1vGP4cdfO4gSZnZa8N/MBbkdW6NKuYTJNdpsvZQBeHrD3oXpOi AEZA== X-Forwarded-Encrypted: i=1; AFNElJ+zzrf2EO6LoOZT6X8R1XMc6HJIAopNX8GeXcYq+7m09zUn7VzZJQsa3Q4D6RgNrprl293tiF3OSaPcTt0=@vger.kernel.org X-Gm-Message-State: AOJu0YzvBLS3YMNUfIqK3pghv0HnzjyNo//5UZL02VjciUv/L+4YlPn3 Adm+D7ZXNGHmvq0lRidkGmDp1fkIOR95wX0Jueiwc0DLKmYysQEFMd3LWlw8eTSX1lLyTsNAEPV F3hLHJrw+0e+LMdtztX2UKTfWym9arrIIFLhV/UbpGaHygp64jMc7OkjYtA4AhejNUQ== X-Gm-Gg: AfdE7ckMzuX61wyjwiu7nn925NOX+QzakPYsyvoCnZs4S2TEvZFT3bRoEoD4wPjAXJL U4Xfi7ZivRCNkmM/dVnWJgo9VKMpbiC8S9Hj0UfV75jsWJPghhGwrkHsJ3YTgI9wHxEYWuiKxOW uNsJOEnGVPbWDl3mz6kw6InCGEcygNvv4eIJszD+j2/9QGvHnNYPLZMrb8uwaRtRk/wZaOLSwep ypio6ov33MbAScGrMCwmmZRdTEZgOk1+6zE4FlrnnH2bYXnMmR36zaZYIScOFUVYGT9Teg31l6/ 2D/4dvWWhQAogEfDgnX6qZhHyV5/KbwCpxy4zJ0G+7bsTsIIRDozZCqUQ7UCZFW70UHES/kFIGz epeH4qG7xUYCUIw9/BtkLgUo8kheuIoNlGPux+/jo8UAqsb+jnCTyKHOfPwaIVz8rPvW7eNgEbH g81Qr93g== X-Received: by 2002:a05:6820:1907:b0:69e:98c0:a9c with SMTP id 006d021491bc7-6a35538e0e0mr4077695eaf.11.1783448964555; Tue, 07 Jul 2026 11:29:24 -0700 (PDT) X-Received: by 2002:a05:6820:1907:b0:69e:98c0:a9c with SMTP id 006d021491bc7-6a35538e0e0mr4077668eaf.11.1783448963879; Tue, 07 Jul 2026 11:29:23 -0700 (PDT) Received: from chaithco-thinkpadp16vgen1.rmtusco.csb.cloud (184-96-191-56.hlrn.qwest.net. [184.96.191.56]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-44cfb55907dsm13550509fac.11.2026.07.07.11.29.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jul 2026 11:29:22 -0700 (PDT) From: Charles Haithcock To: muchun.song@linux.dev, osalvador@suse.de, akpm@linux-foundation.org Cc: Charles Haithcock , david@kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, arozansk@redhat.com, Joshua Hahn , Usama Arif Subject: [PATCH 2/2] selftests/mm: add surplus hugetlb mempolicy reservation tests Date: Tue, 7 Jul 2026 12:28:56 -0600 Message-ID: <20260707182900.483757-3-chaithco@redhat.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260707182900.483757-1-chaithco@redhat.com> References: <20260707182900.483757-1-chaithco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Verify that surplus hugetlb reservasations in the presence of mempolicies will prefer the nodes in the policy. A couple tests do basic sanity checking to validate if the rest of the tests should or should not work. Note that this does not test the edge cases where reservations would fall back to other nodes; the intent of this patch series is to "do better" with reserving surplus pages within mempolicies. Later efforts may further improve this and result in testing those edge cases as false negatives. Suggested-by: Joshua Hahn Suggested-by: Usama Arif Signed-off-by: Charles Haithcock --- - v1: Modified `needed` calculation to use `allowed_mems_nr(h)` in order to consider free hugetlb pages in our mempolicy. - v2: Folded in Joshua Hahn's recommendation [1] to further modify `needed` calculation to take the max of either the available hugetlb pages in the mempolicy or the globally available hugetlb pages. Allows allocations to prioritize nodes in the mempolicy but can still fall back to offnode allocations. Also added selftests to check only for the edgecase which caused this to initially be reported and sanity checks. - v3: Split the change and tests into separate patches and clean up code to better align with kernel style guidelines. Likewise, fold in Usama Arif's correction [2]. [1] https://lore.kernel.org/all/20260602152022.2673803-1-joshua.hahnjy@gmail.com/ [2] https://lore.kernel.org/linux-mm/20260624144600.8159-1-usama.arif@linux.dev/ tools/testing/selftests/mm/Makefile | 3 + .../selftests/mm/hugetlb_surplus_mempolicy.c | 491 ++++++++++++++++++ tools/testing/selftests/mm/run_vmtests.sh | 1 + 3 files changed, 495 insertions(+) create mode 100644 tools/testing/selftests/mm/hugetlb_surplus_mempolicy.c diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index cd24596cdd..40de0938f3 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 0000000000..e737befcd0 --- /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 +#include +#include +#include +#include +#include + +#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 c17b133a81..cd368ce590 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) -- 2.55.0