From nobody Wed Oct 1 23:30:08 2025 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A71782BDC2F for ; Fri, 26 Sep 2025 09:35:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758879305; cv=none; b=XahrplLZkevdc+oj1ChBxXaOhsdJOHkG05ghnddDM9cGOopmqE1XYA1nT/VmIM3K5ZjIfXkGp1N4/7eVQLuY9PjYL+PjXZS7vVZKsnZAb8i/rJOUwARExTsEmSuHshOg5jWTGDYzjbzKQc9w+3KU7EmG5fDr5+aHz9LjFaN/HLI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758879305; c=relaxed/simple; bh=HBmJaZcDUZzcrC81eiCJDGIY48KrzLAuQ2+UCr8xkqo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I4VZ/zPXkYgIPXL2uJuK6S76x/XI0U/lLWttyi0xofb9qzt8f4ZVdWSALEfIbHsjUUllCeT4VUY+b3q94YeRQM7s57PzNhqPJWm7Dnhj2K71edBLLIQ/olYiIrOankF/litHOHDx9KMBFI8nFeH4JTVeO8nBxISibln10Ppq1/8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Tuj7WRGm; arc=none smtp.client-ip=209.85.215.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Tuj7WRGm" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-b551b040930so1328489a12.2 for ; Fri, 26 Sep 2025 02:35:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758879303; x=1759484103; 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; bh=KejkPMgGRvReGxuBAv36+hrv4MPOBVSti8PCSW2auKE=; b=Tuj7WRGmflzMYd55lgdtWQ6wdMvkJpx++Rra89hqoM8ukhf9f0/t4A01+2JuRlHwWA UAgsA5cAo8SdBBVVOj6UIRZxXDq109d4lJEJn2S+nMyj3EOVi+sKvmJ3OuR9n+WmdJNZ GWFhNbCgCOdOTesC2WjK127YhRwTUCpPh+lFLK4uxcV3MZmifbnOR2sWvRXJY0IoSHDA WX/eY1SFqZkxvbilusCNnlIZcJBEpOWu6lYdhd+2ii6EVWMCqzMBvK+bBS+Czs3bIB88 tbfspq7iEE9h9QT0dVn3H26PORBtIHURc5Ftudb314SctaqUQI7Ed2Db1aOZ/M60/d7G OmzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758879303; x=1759484103; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KejkPMgGRvReGxuBAv36+hrv4MPOBVSti8PCSW2auKE=; b=a0EgrWsOy0N8b1to3RjhcG70Y/D2eiJa23mgUenPH7NfAPfyJ6YJ/16ix2UM+1LKgF k87nglyXCn88pT6FmBQUlFaGfnfggUKl4fmk4F8VOsP8nXsR/qZt7An0eCoP3P1GZDl1 gMoMVzNaYqfzqitjVR5nXO4py3VhDZVpCRiIDan42x4jqIYvgVD3kTNSinQdFSIVnizt rl+m/KHRQTyvXxo+F+NROSR/hMMVRG5PdKYu8+fKCpv92EYVOAkQKUpVEc/Q0qOS47Co CIWcK4eyV40QiAfsOWN6Jvuk3nc9263SM6h2KRRB9zpMnp/AKPeDDMJmGkSHkwJuwJbs JAnA== X-Forwarded-Encrypted: i=1; AJvYcCVyUQSaFTIm3vMGMA1QWbepg9ZfYQ3+Kimwf90RT8l60X/4GqX2CCpSeE9KG0AIScZv7buZiTYBEQ6U8xw=@vger.kernel.org X-Gm-Message-State: AOJu0YwzBWJcnxPcVnfpHCp+3/bYiGoZPwKbWjecNY8RCnC2QeMtxsKl C/YbR1asSUO/gOLRPwrsQ4vdVMi1cHRR/ULddw38R4QBvMVy1pFh/ADh X-Gm-Gg: ASbGnctZ7egPVymPoPhJARXKB11PNkwGALceXZwirpIVa7Z2CHI76sLlFQRPEi22o0U 4zFWwLAYELvoN9a0HarUxaU6jUmDOpJzMn5/TcnJhBQtLoCpyxDEDCCWF0JeZvSR/cZvZmjlfDD vqx8CiUZZx3A+XbDI+oP6HYkYuXDHQPbchu3pOjTB7Uc9bFydjLZDPMDu3dV0IQROj+UggJW7IN ffu/wETwgO7Vn+zrE+MU6Cl3c/eUw3En0bLVj3jZVZTLhhUz60Ixn7yYNix2mO+7ZdXdWte1Ao2 ikJPoqF2hAlvitsJZo1B7wMsosdN76pPNPMmySSHDzdNRMbftWZs54880P4CfT6albW0Dj/uHos aYr0VpEe/mmzRJ60uT/bJmbPjZrF6fHyzQXpQuJJkgH3WtmbsIqrJS+pohQj42HslOZ6wDkf2K9 Qby96rzy3qalfh X-Google-Smtp-Source: AGHT+IHJMvMRnfOqzEak3qmEUf11dLxaZn5jFt91kzwtU8AVAcW525LzqoVivLg7u9BwGX2hLSfT8w== X-Received: by 2002:a17:902:e749:b0:27d:6cdc:99e4 with SMTP id d9443c01a7336-27ed49e6cacmr73186945ad.21.1758879303020; Fri, 26 Sep 2025 02:35:03 -0700 (PDT) Received: from localhost.localdomain ([2409:891f:1c21:566:e1d1:c082:790c:7be6]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-27ed66cda43sm49247475ad.25.2025.09.26.02.34.55 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Fri, 26 Sep 2025 02:35:02 -0700 (PDT) From: Yafang Shao To: akpm@linux-foundation.org, david@redhat.com, ziy@nvidia.com, baolin.wang@linux.alibaba.com, lorenzo.stoakes@oracle.com, Liam.Howlett@oracle.com, npache@redhat.com, ryan.roberts@arm.com, dev.jain@arm.com, hannes@cmpxchg.org, usamaarif642@gmail.com, gutierrez.asier@huawei-partners.com, willy@infradead.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, ameryhung@gmail.com, rientjes@google.com, corbet@lwn.net, 21cnbao@gmail.com, shakeel.butt@linux.dev, tj@kernel.org, lance.yang@linux.dev Cc: bpf@vger.kernel.org, linux-mm@kvack.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Yafang Shao Subject: [PATCH v8 mm-new 09/12] selftests/bpf: add a simple BPF based THP policy Date: Fri, 26 Sep 2025 17:33:40 +0800 Message-Id: <20250926093343.1000-10-laoar.shao@gmail.com> X-Mailer: git-send-email 2.37.1 (Apple Git-137.1) In-Reply-To: <20250926093343.1000-1-laoar.shao@gmail.com> References: <20250926093343.1000-1-laoar.shao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This test case implements a basic THP policy that sets THPeligible to 1 for a specific task and to 0 for all others. I selected THPeligible for verification because its straightforward nature makes it ideal for validating the BPF THP policy functionality. Signed-off-by: Yafang Shao --- MAINTAINERS | 2 + tools/testing/selftests/bpf/config | 3 + .../selftests/bpf/prog_tests/thp_adjust.c | 258 ++++++++++++++++++ .../selftests/bpf/progs/test_thp_adjust.c | 41 +++ 4 files changed, 304 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/thp_adjust.c create mode 100644 tools/testing/selftests/bpf/progs/test_thp_adjust.c diff --git a/MAINTAINERS b/MAINTAINERS index 7be34b2a64fd..c1219bcd27c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16260,6 +16260,8 @@ F: mm/huge_memory.c F: mm/huge_memory_bpf.c F: mm/khugepaged.c F: mm/mm_slot.h +F: tools/testing/selftests/bpf/prog_tests/thp_adjust.c +F: tools/testing/selftests/bpf/progs/test_thp_adjust* F: tools/testing/selftests/mm/khugepaged.c F: tools/testing/selftests/mm/split_huge_page_test.c F: tools/testing/selftests/mm/transhuge-stress.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/b= pf/config index 8916ab814a3e..7ccb9809e276 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -26,6 +26,7 @@ CONFIG_DMABUF_HEAPS=3Dy CONFIG_DMABUF_HEAPS_SYSTEM=3Dy CONFIG_DUMMY=3Dy CONFIG_DYNAMIC_FTRACE=3Dy +CONFIG_BPF_THP_GET_ORDER_EXPERIMENTAL=3Dy CONFIG_FPROBE=3Dy CONFIG_FTRACE_SYSCALLS=3Dy CONFIG_FUNCTION_ERROR_INJECTION=3Dy @@ -51,6 +52,7 @@ CONFIG_IPV6_TUNNEL=3Dy CONFIG_KEYS=3Dy CONFIG_LIRC=3Dy CONFIG_LWTUNNEL=3Dy +CONFIG_MEMCG=3Dy CONFIG_MODULE_SIG=3Dy CONFIG_MODULE_SRCVERSION_ALL=3Dy CONFIG_MODULE_UNLOAD=3Dy @@ -114,6 +116,7 @@ CONFIG_SECURITY=3Dy CONFIG_SECURITYFS=3Dy CONFIG_SYN_COOKIES=3Dy CONFIG_TEST_BPF=3Dm +CONFIG_TRANSPARENT_HUGEPAGE=3Dy CONFIG_UDMABUF=3Dy CONFIG_USERFAULTFD=3Dy CONFIG_VSOCKETS=3Dy diff --git a/tools/testing/selftests/bpf/prog_tests/thp_adjust.c b/tools/te= sting/selftests/bpf/prog_tests/thp_adjust.c new file mode 100644 index 000000000000..b14f57040654 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/thp_adjust.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include "test_thp_adjust.skel.h" + +#define LEN (16 * 1024 * 1024) /* 16MB */ +#define THP_ENABLED_FILE "/sys/kernel/mm/transparent_hugepage/enabled" +#define PMD_SIZE_FILE "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" + +static struct test_thp_adjust *skel; +static char old_mode[32]; +static long pagesize; + +static int thp_mode_save(void) +{ + const char *start, *end; + char buf[128]; + int fd, err; + size_t len; + + fd =3D open(THP_ENABLED_FILE, O_RDONLY); + if (fd =3D=3D -1) + return -1; + + err =3D read(fd, buf, sizeof(buf) - 1); + if (err =3D=3D -1) + goto close; + + start =3D strchr(buf, '['); + end =3D start ? strchr(start, ']') : NULL; + if (!start || !end || end <=3D start) { + err =3D -1; + goto close; + } + + len =3D end - start - 1; + if (len >=3D sizeof(old_mode)) + len =3D sizeof(old_mode) - 1; + strncpy(old_mode, start + 1, len); + old_mode[len] =3D '\0'; + +close: + close(fd); + return err; +} + +static int thp_mode_set(const char *desired_mode) +{ + int fd, err; + + fd =3D open(THP_ENABLED_FILE, O_RDWR); + if (fd =3D=3D -1) + return -1; + + err =3D write(fd, desired_mode, strlen(desired_mode)); + close(fd); + return err; +} + +static int thp_mode_reset(void) +{ + int fd, err; + + fd =3D open(THP_ENABLED_FILE, O_WRONLY); + if (fd =3D=3D -1) + return -1; + + err =3D write(fd, old_mode, strlen(old_mode)); + close(fd); + return err; +} + +static char *thp_alloc(void) +{ + char *addr; + int err, i; + + addr =3D mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, = -1, 0); + if (addr =3D=3D MAP_FAILED) + return NULL; + + err =3D madvise(addr, LEN, MADV_HUGEPAGE); + if (err =3D=3D -1) + goto unmap; + + /* Accessing a single byte within a page is sufficient to trigger a page = fault. */ + for (i =3D 0; i < LEN; i +=3D pagesize) + addr[i] =3D 1; + return addr; + +unmap: + munmap(addr, LEN); + return NULL; +} + +static void thp_free(char *ptr) +{ + munmap(ptr, LEN); +} + +static int get_pmd_order(void) +{ + ssize_t bytes_read, size; + int fd, order, ret =3D -1; + char buf[64], *endptr; + + fd =3D open(PMD_SIZE_FILE, O_RDONLY); + if (fd < 0) + return -1; + + bytes_read =3D read(fd, buf, sizeof(buf) - 1); + if (bytes_read <=3D 0) + goto close_fd; + + /* Remove potential newline character */ + if (buf[bytes_read - 1] =3D=3D '\n') + buf[bytes_read - 1] =3D '\0'; + + size =3D strtoul(buf, &endptr, 10); + if (endptr =3D=3D buf || *endptr !=3D '\0') + goto close_fd; + if (size % pagesize !=3D 0) + goto close_fd; + ret =3D size / pagesize; + if ((ret & (ret - 1)) =3D=3D 0) { + order =3D 0; + while (ret > 1) { + ret >>=3D 1; + order++; + } + ret =3D order; + } + +close_fd: + close(fd); + return ret; +} + +static int get_thp_eligible(pid_t pid, unsigned long addr) +{ + int this_vma =3D 0, eligible =3D -1; + unsigned long start, end; + char smaps_path[64]; + FILE *smaps_file; + char line[4096]; + + snprintf(smaps_path, sizeof(smaps_path), "/proc/%d/smaps", pid); + smaps_file =3D fopen(smaps_path, "r"); + if (!smaps_file) + return -1; + + while (fgets(line, sizeof(line), smaps_file)) { + if (sscanf(line, "%lx-%lx", &start, &end) =3D=3D 2) { + /* addr is monotonic */ + if (addr < start) + break; + this_vma =3D (addr >=3D start && addr < end) ? 1 : 0; + continue; + } + + if (!this_vma) + continue; + + if (strstr(line, "THPeligible:")) { + sscanf(line, "THPeligible: %d", &eligible); + break; + } + } + + fclose(smaps_file); + return eligible; +} + +static void subtest_thp_eligible(void) +{ + struct bpf_link *ops_link; + int elighble; + pid_t pid; + char *ptr; + + ops_link =3D bpf_map__attach_struct_ops(skel->maps.thp_eligible_ops); + if (!ASSERT_OK_PTR(ops_link, "attach struct_ops")) + return; + + pid =3D getpid(); + ptr =3D thp_alloc(); + if (!ASSERT_OK_PTR(ptr, "THP alloc")) + goto detach; + + skel->bss->pid_eligible =3D pid; + elighble =3D get_thp_eligible(pid, (unsigned long)ptr); + ASSERT_EQ(elighble, 1, "THPeligible"); + + skel->bss->pid_eligible =3D 0; + skel->bss->pid_not_eligible =3D pid; + elighble =3D get_thp_eligible(pid, (unsigned long)ptr); + ASSERT_EQ(elighble, 0, "THP not eligible"); + + skel->bss->pid_eligible =3D 0; + skel->bss->pid_not_eligible =3D 0; + elighble =3D get_thp_eligible(pid, (unsigned long)ptr); + ASSERT_EQ(elighble, 0, "THP not eligible"); + + thp_free(ptr); +detach: + bpf_link__destroy(ops_link); +} + +static int thp_adjust_setup(void) +{ + int err =3D -1, pmd_order; + + pagesize =3D sysconf(_SC_PAGESIZE); + pmd_order =3D get_pmd_order(); + if (!ASSERT_NEQ(pmd_order, -1, "get_pmd_order")) + return -1; + + if (!ASSERT_NEQ(thp_mode_save(), -1, "THP mode save")) + return -1; + if (!ASSERT_GE(thp_mode_set("madvise"), 0, "THP mode set")) + return -1; + + skel =3D test_thp_adjust__open(); + if (!ASSERT_OK_PTR(skel, "open")) + goto thp_reset; + + skel->bss->pmd_order =3D pmd_order; + + err =3D test_thp_adjust__load(skel); + if (!ASSERT_OK(err, "load")) + goto destroy; + return 0; + +destroy: + test_thp_adjust__destroy(skel); +thp_reset: + ASSERT_GE(thp_mode_reset(), 0, "THP mode reset"); + return err; +} + +static void thp_adjust_destroy(void) +{ + test_thp_adjust__destroy(skel); + ASSERT_GE(thp_mode_reset(), 0, "THP mode reset"); +} + +void test_thp_adjust(void) +{ + if (thp_adjust_setup() =3D=3D -1) + return; + + if (test__start_subtest("thp_eligible")) + subtest_thp_eligible(); + + thp_adjust_destroy(); +} diff --git a/tools/testing/selftests/bpf/progs/test_thp_adjust.c b/tools/te= sting/selftests/bpf/progs/test_thp_adjust.c new file mode 100644 index 000000000000..ed8c510693a0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_thp_adjust.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") =3D "GPL"; + +int pid_not_eligible, pid_eligible; +int pmd_order; + +SEC("struct_ops/thp_get_order") +int BPF_PROG(thp_eligible, struct vm_area_struct *vma, enum tva_type tva_t= ype, + unsigned long orders) +{ + struct mm_struct *mm =3D vma->vm_mm; + int suggested_order =3D 0; + struct task_struct *p; + + if (tva_type !=3D TVA_SMAPS) + return 0; + + if (!mm) + return 0; + + /* This BPF hook is already under RCU */ + p =3D mm->owner; + if (!p || (p->pid !=3D pid_eligible && p->pid !=3D pid_not_eligible)) + return 0; + + if (p->pid =3D=3D pid_eligible) + suggested_order =3D pmd_order; + else + suggested_order =3D 30; /* invalid order */ + return suggested_order; +} + +SEC(".struct_ops.link") +struct bpf_thp_ops thp_eligible_ops =3D { + .thp_get_order =3D (void *)thp_eligible, +}; --=20 2.47.3