From nobody Mon Jun 8 17:39:50 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 28BE63F1AAB; Wed, 27 May 2026 14:25:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891904; cv=none; b=k0OtJnMmReQthlKj2VMB12A+u4J8zwZGtbTOLvaiWjpfq411MMQstOrPGxGXz0P9SLc6eJ8GTZx4ZuK3+WmFqq3R3+7qb/L4R0kkRMACpd9+UnyEn6JBeApP62/J8dKPbJ3ch/fJQkZQbBmhXR6oimxSCqJYeck8MQvp2+n//x4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891904; c=relaxed/simple; bh=r1N+oOV1OlUKMcFglRUZutswFqoJ0C5j+K1Zp/Mn3c4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=erRcBmkZopJGc9VnQgRpPn9K7RWohfPKG//gdqaZLZpsgPUjt/9h4d7KxtJpgyVtt3L8wv7sOuTKRmIcgnOy6DM9vnZDxhLcMK18/uFlq6m58jLTkywffYi2Cu0hOcWhun7vA4hbFew8iWnXpxXpMAmeZXTePU+yf85zK1S9sQA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=gUwXZ3mm; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="gUwXZ3mm" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2100127B5; Wed, 27 May 2026 07:24:56 -0700 (PDT) Received: from a081061.blr.arm.com (a081061.arm.com [10.164.19.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id B109A3F905; Wed, 27 May 2026 07:24:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779891901; bh=r1N+oOV1OlUKMcFglRUZutswFqoJ0C5j+K1Zp/Mn3c4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gUwXZ3mmCqtJfFefDf/b0Hu3mBdt17yYnA3dkIQnm7xJhiL5034AJPTN0tV+WmjPM 3su6wjQTtCvSk+b+feKS4ElvJ/UEBip5R/MisPD/hLK0upNP/pUadFVL3ZyuWK6S0K uVCQsNDHC1mEcewcrbJxAthKvl8VDNEU0p9fW3RQ= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Jason Gunthorpe , John Hubbard , Peter Xu , Leon Romanovsky , Jonathan Corbet , Shuah Khan , Mark Brown , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Sarthak Sharma Subject: [PATCH v4 1/5] selftests/mm: make file helpers return errors Date: Wed, 27 May 2026 19:54:28 +0530 Message-ID: <20260527142432.230127-2-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260527142432.230127-1-sarthak.sharma@arm.com> References: <20260527142432.230127-1-sarthak.sharma@arm.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" Change read_file(), write_file(), read_num() and write_num() in vm_util.c to report failures to callers instead of exiting from the helper. Update the existing mm selftest callers to report failures through kselftest helpers. This prepares the helpers to be moved to tools/lib/mm without carrying selftest-specific process-exit behaviour into the shared implementation. Also make read_file() return a negative errno on failure instead of 0, so callers can distinguish a successful read from an I/O error. Signed-off-by: Sarthak Sharma --- .../testing/selftests/mm/hugepage_settings.c | 85 ++++++++++++++++--- tools/testing/selftests/mm/khugepaged.c | 13 ++- .../selftests/mm/split_huge_page_test.c | 5 +- tools/testing/selftests/mm/vm_util.c | 63 +++++++++----- tools/testing/selftests/mm/vm_util.h | 6 +- 5 files changed, 131 insertions(+), 41 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing= /selftests/mm/hugepage_settings.c index 2eab2110ac6a..c2f97fe97e58 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include @@ -61,7 +62,9 @@ int thp_read_string(const char *name, const char * const = strings[]) exit(EXIT_FAILURE); } =20 - if (!read_file(path, buf, sizeof(buf))) { + ret =3D read_file(path, buf, sizeof(buf)); + if (ret < 0) { + errno =3D -ret; perror(path); exit(EXIT_FAILURE); } @@ -103,12 +106,18 @@ void thp_write_string(const char *name, const char *v= al) printf("%s: Pathname is too long\n", __func__); exit(EXIT_FAILURE); } - write_file(path, val, strlen(val) + 1); + ret =3D write_file(path, val, strlen(val) + 1); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } } =20 unsigned long thp_read_num(const char *name) { char path[PATH_MAX]; + unsigned long num; int ret; =20 ret =3D snprintf(path, PATH_MAX, THP_SYSFS "%s", name); @@ -116,7 +125,14 @@ unsigned long thp_read_num(const char *name) printf("%s: Pathname is too long\n", __func__); exit(EXIT_FAILURE); } - return read_num(path); + ret =3D read_num(path, &num); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } + + return num; } =20 void thp_write_num(const char *name, unsigned long num) @@ -129,7 +145,12 @@ void thp_write_num(const char *name, unsigned long num) printf("%s: Pathname is too long\n", __func__); exit(EXIT_FAILURE); } - write_num(path, num); + ret =3D write_num(path, num); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } } =20 void thp_read_settings(struct thp_settings *settings) @@ -157,8 +178,16 @@ void thp_read_settings(struct thp_settings *settings) .max_ptes_shared =3D thp_read_num("khugepaged/max_ptes_shared"), .pages_to_scan =3D thp_read_num("khugepaged/pages_to_scan"), }; - if (dev_queue_read_ahead_path[0]) - settings->read_ahead_kb =3D read_num(dev_queue_read_ahead_path); + if (dev_queue_read_ahead_path[0]) { + int ret =3D read_num(dev_queue_read_ahead_path, + &settings->read_ahead_kb); + + if (ret < 0) { + errno =3D -ret; + perror(dev_queue_read_ahead_path); + exit(EXIT_FAILURE); + } + } =20 for (i =3D 0; i < NR_ORDERS; i++) { if (!((1 << i) & orders)) { @@ -208,8 +237,16 @@ void thp_write_settings(struct thp_settings *settings) thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared); thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan); =20 - if (dev_queue_read_ahead_path[0]) - write_num(dev_queue_read_ahead_path, settings->read_ahead_kb); + if (dev_queue_read_ahead_path[0]) { + int ret =3D write_num(dev_queue_read_ahead_path, + settings->read_ahead_kb); + + if (ret < 0) { + errno =3D -ret; + perror(dev_queue_read_ahead_path); + exit(EXIT_FAILURE); + } + } =20 for (i =3D 0; i < NR_ORDERS; i++) { if (!((1 << i) & orders)) @@ -307,7 +344,7 @@ static unsigned long __thp_supported_orders(bool is_shm= em) } =20 ret =3D read_file(path, buf, sizeof(buf)); - if (ret) + if (ret > 0) orders |=3D 1UL << i; } =20 @@ -425,28 +462,52 @@ static void hugetlb_sysfs_path(char *buf, size_t bufl= en, unsigned long hugetlb_nr_pages(unsigned long size) { char path[PATH_MAX]; + unsigned long nr; + int ret; =20 hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); =20 - return read_num(path); + ret =3D read_num(path, &nr); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } + + return nr; } =20 void hugetlb_set_nr_pages(unsigned long size, unsigned long nr) { char path[PATH_MAX]; + int ret; =20 hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); =20 - write_num(path, nr); + ret =3D write_num(path, nr); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } } =20 unsigned long hugetlb_free_pages(unsigned long size) { char path[PATH_MAX]; + unsigned long nr; + int ret; =20 hugetlb_sysfs_path(path, sizeof(path), size, "free_hugepages"); =20 - return read_num(path); + ret =3D read_num(path, &nr); + if (ret < 0) { + errno =3D -ret; + perror(path); + exit(EXIT_FAILURE); + } + + return nr; } =20 static bool __hugetlb_setup(unsigned long size, unsigned long nr) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selfte= sts/mm/khugepaged.c index 10e8dedcb087..bc923148e983 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -119,6 +119,7 @@ static void get_finfo(const char *dir) char buf[1 << 10]; char path[PATH_MAX]; char *str, *end; + int ret; =20 finfo.dir =3D dir; stat(finfo.dir, &path_stat); @@ -138,8 +139,9 @@ static void get_finfo(const char *dir) major(path_stat.st_dev), minor(path_stat.st_dev)) >=3D sizeof(path)) ksft_exit_fail_msg("%s: Pathname is too long\n", __func__); - if (read_file(path, buf, sizeof(buf)) < 0) - ksft_exit_fail_perror("read_file(read_num)"); + ret =3D read_file(path, buf, sizeof(buf)); + if (ret < 0) + ksft_exit_fail_msg("read_file(%s): %s\n", path, strerror(-ret)); if (strstr(buf, "DEVTYPE=3Ddisk")) { /* Found it */ if (snprintf(finfo.dev_queue_read_ahead_path, @@ -319,7 +321,7 @@ static void *file_setup_area_common(int nr_hpages, enum= file_setup_ops setup) { const int open_opt =3D setup =3D=3D FILE_SETUP_READ_ONLY_FS ? O_RDONLY : = O_RDWR; const int mmap_prot =3D setup =3D=3D FILE_SETUP_READ_ONLY_FS ? PROT_READ = : (PROT_READ | PROT_WRITE); - int fd; + int fd, ret; void *p; unsigned long size; =20 @@ -363,7 +365,10 @@ static void *file_setup_area_common(int nr_hpages, enu= m file_setup_ops setup) ksft_exit_fail_perror("mmap()"); =20 /* Drop page cache */ - write_file("/proc/sys/vm/drop_caches", "3", 2); + ret =3D write_file("/proc/sys/vm/drop_caches", "3", 2); + if (ret) + ksft_exit_fail_msg("write_file(drop_caches): %s\n", + strerror(-ret)); success("OK"); return p; } diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/test= ing/selftests/mm/split_huge_page_test.c index 32b991472f74..98236db7c813 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -269,7 +269,10 @@ static void write_debugfs(const char *fmt, ...) if (ret >=3D INPUT_MAX) ksft_exit_fail_msg("%s: Debugfs input is too long\n", __func__); =20 - write_file(SPLIT_DEBUGFS, input, ret + 1); + ret =3D write_file(SPLIT_DEBUGFS, input, ret + 1); + if (ret) + ksft_exit_fail_msg("write_file(%s): %s\n", SPLIT_DEBUGFS, + strerror(-ret)); } =20 static char *allocate_zero_filled_hugepage(size_t len) diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests= /mm/vm_util.c index 311fc5b4513e..290e5c29123e 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -703,62 +703,72 @@ int read_file(const char *path, char *buf, size_t buf= len) int fd; ssize_t numread; =20 + if (buflen < 2) + return -EINVAL; + fd =3D open(path, O_RDONLY); if (fd =3D=3D -1) - return 0; + return -errno; =20 numread =3D read(fd, buf, buflen - 1); if (numread < 1) { + int err =3D numread ? -errno : -ENODATA; + close(fd); - return 0; + return err; } =20 buf[numread] =3D '\0'; close(fd); =20 - return (unsigned int) numread; + return (int)numread; } =20 -void write_file(const char *path, const char *buf, size_t buflen) +int write_file(const char *path, const char *buf, size_t buflen) { int fd, saved_errno; ssize_t numwritten; =20 if (buflen < 2) - ksft_exit_fail_msg("Incorrect buffer len: %zu\n", buflen); + return -EINVAL; =20 fd =3D open(path, O_WRONLY); if (fd =3D=3D -1) - ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno)); + return -errno; =20 numwritten =3D write(fd, buf, buflen - 1); saved_errno =3D errno; close(fd); - errno =3D saved_errno; if (numwritten < 0) - ksft_exit_fail_msg("%s write(%.*s) failed: %s\n", path, (int)(buflen - 1= ), - buf, strerror(errno)); + return -saved_errno; if (numwritten !=3D buflen - 1) - ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got= %zd bytes\n", - path, (int)(buflen - 1), buf, buflen - 1, numwritten); + return -EIO; + + return 0; } =20 -unsigned long read_num(const char *path) +int read_num(const char *path, unsigned long *num) { char buf[21]; + int ret; =20 - if (read_file(path, buf, sizeof(buf)) < 0) - ksft_exit_fail_perror("read_file()"); + if (!num) + return -EINVAL; =20 - return strtoul(buf, NULL, 10); + ret =3D read_file(path, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *num =3D strtoul(buf, NULL, 10); + return 0; } =20 -void write_num(const char *path, unsigned long num) +int write_num(const char *path, unsigned long num) { char buf[21]; =20 sprintf(buf, "%lu", num); - write_file(path, buf, strlen(buf) + 1); + return write_file(path, buf, strlen(buf) + 1); } =20 static unsigned long shmall, shmmax; @@ -775,16 +785,27 @@ void shm_limits_prepare(unsigned long length) { unsigned long nr =3D length / psize(); unsigned long val; + int ret; =20 - val =3D read_num("/proc/sys/kernel/shmmax"); + ret =3D read_num("/proc/sys/kernel/shmmax", &val); + if (ret) + ksft_exit_fail_msg("read_num(shmmax): %s\n", strerror(-ret)); if (val < length) { - write_num("/proc/sys/kernel/shmmax", length); + ret =3D write_num("/proc/sys/kernel/shmmax", length); + if (ret) + ksft_exit_fail_msg("write_num(shmmax): %s\n", + strerror(-ret)); shmmax =3D val; } =20 - val =3D read_num("/proc/sys/kernel/shmall"); + ret =3D read_num("/proc/sys/kernel/shmall", &val); + if (ret) + ksft_exit_fail_msg("read_num(shmall): %s\n", strerror(-ret)); if (val < nr) { - write_num("/proc/sys/kernel/shmall", nr); + ret =3D write_num("/proc/sys/kernel/shmall", nr); + if (ret) + ksft_exit_fail_msg("write_num(shmall): %s\n", + strerror(-ret)); shmall =3D val; } } diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests= /mm/vm_util.h index ea8fc8fdf0eb..28c3d7c1faed 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -164,10 +164,10 @@ int unpoison_memory(unsigned long pfn); #define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) !=3D 0) #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1)) =20 -void write_file(const char *path, const char *buf, size_t buflen); int read_file(const char *path, char *buf, size_t buflen); -unsigned long read_num(const char *path); -void write_num(const char *path, unsigned long num); +int write_file(const char *path, const char *buf, size_t buflen); +int read_num(const char *path, unsigned long *num); +int write_num(const char *path, unsigned long num); =20 void shm_limits_prepare(unsigned long length); void __shm_limits_restore(void); --=20 2.39.5 From nobody Mon Jun 8 17:39:50 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5353C2E1EFC; Wed, 27 May 2026 14:25:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891914; cv=none; b=E/aKk9Y6gpPHlYabNhsDbNMLS2t0Fkj5C+9FoOslhYgEaIMkHvsNvwvHQtdAR6aYFAyTbupMSrUCRK6ySzXU1OdS8gcSXWRgVAmJKn6NGy2x3+BjK4p4trqxbV8uurhpEHSe0+l10sdv8j0KV/j3Zal0fI0gQczKODOpGPR88x0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891914; c=relaxed/simple; bh=HbanYL7EP3DG/8UNgIVFx2Gl5bPxfKd9NFC21trX438=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K2CFlKI1e1tjIwuJJ3IA1fXrAJnRfNmtBhIbJlo7ar2Ngw59lwYOhZBq9+sX0Q3DG8FSLvHQUJBrHCtBGHpA4TQYXnwHI65sHB3EXE0gRJ51Cqq7iG7kz65Z3JdjdsMHwaQdo4KNQv/Ro7pPAT2VXxuluedbiq6zE8C3TY+nb78= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=mz0g71Zg; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="mz0g71Zg" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 468F827B5; Wed, 27 May 2026 07:25:06 -0700 (PDT) Received: from a081061.blr.arm.com (a081061.arm.com [10.164.19.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id D47653F905; Wed, 27 May 2026 07:25:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779891911; bh=HbanYL7EP3DG/8UNgIVFx2Gl5bPxfKd9NFC21trX438=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mz0g71ZgF2HVY6JVKWa0LeiCiqk2vNWfsaOWj+wQIBZCW7szyaL6CllcczkiPQE5M YcGauLzycdIB6EIOG7Gl+J6VjwHx/ThHHDtRgaqOmR6Mihn2TYQsQ9M6lc03bEDhIb P3uhnW1uBwFAsy3MZhrdvn2ZBAiX5+J4KdjILt9A= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Jason Gunthorpe , John Hubbard , Peter Xu , Leon Romanovsky , Jonathan Corbet , Shuah Khan , Mark Brown , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Sarthak Sharma Subject: [PATCH v4 2/5] tools/lib/mm: add shared file helpers Date: Wed, 27 May 2026 19:54:29 +0530 Message-ID: <20260527142432.230127-3-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260527142432.230127-1-sarthak.sharma@arm.com> References: <20260527142432.230127-1-sarthak.sharma@arm.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" Move read_file(), write_file(), read_num(), and write_num() out of tools/testing/selftests/mm/vm_util.c into a new shared helper under tools/lib/mm/. These helpers are used by mm selftests today and will also be needed by shared hugepage helpers in subsequent patches. Move them to a generic location so they can be reused outside selftests as well. Keep the helpers exposed to mm selftests through vm_util.h by including the new shared header there, and link the new helper into the selftests/mm build. Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS. Signed-off-by: Sarthak Sharma --- MAINTAINERS | 1 + tools/lib/mm/file_utils.c | 82 ++++++++++++++++++++++++++++ tools/lib/mm/file_utils.h | 12 ++++ tools/testing/selftests/mm/Makefile | 6 +- tools/testing/selftests/mm/vm_util.c | 73 ------------------------- tools/testing/selftests/mm/vm_util.h | 6 +- 6 files changed, 99 insertions(+), 81 deletions(-) create mode 100644 tools/lib/mm/file_utils.c create mode 100644 tools/lib/mm/file_utils.h diff --git a/MAINTAINERS b/MAINTAINERS index 6a123fd3cfd5..5d2f72bbd128 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16936,6 +16936,7 @@ F: mm/memory-tiers.c F: mm/page_idle.c F: mm/pgalloc-track.h F: mm/process_vm_access.c +F: tools/lib/mm/ F: tools/testing/selftests/mm/ =20 MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c new file mode 100644 index 000000000000..00610013e80e --- /dev/null +++ b/tools/lib/mm/file_utils.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#include "file_utils.h" + +int read_file(const char *path, char *buf, size_t buflen) +{ + int fd; + ssize_t numread; + + if (buflen < 2) + return -EINVAL; + + fd =3D open(path, O_RDONLY); + if (fd =3D=3D -1) + return -errno; + + numread =3D read(fd, buf, buflen - 1); + if (numread < 1) { + int err =3D numread ? -errno : -ENODATA; + + close(fd); + return err; + } + + buf[numread] =3D '\0'; + close(fd); + + return (int)numread; +} + +int write_file(const char *path, const char *buf, size_t buflen) +{ + int fd, saved_errno; + ssize_t numwritten; + + if (buflen < 2) + return -EINVAL; + + fd =3D open(path, O_WRONLY); + if (fd =3D=3D -1) + return -errno; + + numwritten =3D write(fd, buf, buflen - 1); + saved_errno =3D errno; + close(fd); + if (numwritten < 0) + return -saved_errno; + if (numwritten !=3D (ssize_t)(buflen - 1)) + return -EIO; + + return 0; +} + +int read_num(const char *path, unsigned long *num) +{ + char buf[21]; + int ret; + + if (!num) + return -EINVAL; + + ret =3D read_file(path, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *num =3D strtoul(buf, NULL, 10); + return 0; +} + +int write_num(const char *path, unsigned long num) +{ + char buf[21]; + + snprintf(buf, sizeof(buf), "%lu", num); + return write_file(path, buf, strlen(buf) + 1); +} diff --git a/tools/lib/mm/file_utils.h b/tools/lib/mm/file_utils.h new file mode 100644 index 000000000000..38576790a342 --- /dev/null +++ b/tools/lib/mm/file_utils.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MM_FILE_UTILS_H__ +#define __MM_FILE_UTILS_H__ + +#include + +int read_file(const char *path, char *buf, size_t buflen); +int write_file(const char *path, const char *buf, size_t buflen); +int read_num(const char *path, unsigned long *num); +int write_num(const char *path, unsigned long num); + +#endif diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/= mm/Makefile index e6df968f0971..b5fb4b6ab31b 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -37,7 +37,7 @@ endif # LDLIBS. MAKEFLAGS +=3D --no-builtin-rules =20 -CFLAGS =3D -Wall -O2 -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(T= OOLS_INCLUDES) +CFLAGS =3D -Wall -O2 -I $(top_srcdir) -I $(top_srcdir)/tools/lib $(EXTRA_C= FLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES) CFLAGS +=3D -Wunreachable-code LDLIBS =3D -lrt -lpthread -lm =20 @@ -187,8 +187,8 @@ TEST_FILES +=3D write_hugetlb_memory.sh =20 include ../lib.mk =20 -$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c -$(TEST_GEN_FILES): vm_util.c hugepage_settings.c +$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/m= m/file_utils.c +$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/m= m/file_utils.c =20 $(OUTPUT)/uffd-stress: uffd-common.c $(OUTPUT)/uffd-unit-tests: uffd-common.c diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests= /mm/vm_util.c index 290e5c29123e..b3ee00d71aa3 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -698,79 +698,6 @@ int unpoison_memory(unsigned long pfn) return ret > 0 ? 0 : -errno; } =20 -int read_file(const char *path, char *buf, size_t buflen) -{ - int fd; - ssize_t numread; - - if (buflen < 2) - return -EINVAL; - - fd =3D open(path, O_RDONLY); - if (fd =3D=3D -1) - return -errno; - - numread =3D read(fd, buf, buflen - 1); - if (numread < 1) { - int err =3D numread ? -errno : -ENODATA; - - close(fd); - return err; - } - - buf[numread] =3D '\0'; - close(fd); - - return (int)numread; -} - -int write_file(const char *path, const char *buf, size_t buflen) -{ - int fd, saved_errno; - ssize_t numwritten; - - if (buflen < 2) - return -EINVAL; - - fd =3D open(path, O_WRONLY); - if (fd =3D=3D -1) - return -errno; - - numwritten =3D write(fd, buf, buflen - 1); - saved_errno =3D errno; - close(fd); - if (numwritten < 0) - return -saved_errno; - if (numwritten !=3D buflen - 1) - return -EIO; - - return 0; -} - -int read_num(const char *path, unsigned long *num) -{ - char buf[21]; - int ret; - - if (!num) - return -EINVAL; - - ret =3D read_file(path, buf, sizeof(buf)); - if (ret < 0) - return ret; - - *num =3D strtoul(buf, NULL, 10); - return 0; -} - -int write_num(const char *path, unsigned long num) -{ - char buf[21]; - - sprintf(buf, "%lu", num); - return write_file(path, buf, strlen(buf) + 1); -} - static unsigned long shmall, shmmax; =20 void __shm_limits_restore(void) diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests= /mm/vm_util.h index 28c3d7c1faed..d45135283732 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -8,6 +8,7 @@ #include /* _SC_PAGESIZE */ #include "kselftest.h" #include +#include =20 #define BIT_ULL(nr) (1ULL << (nr)) #define PM_SOFT_DIRTY BIT_ULL(55) @@ -164,11 +165,6 @@ int unpoison_memory(unsigned long pfn); #define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) !=3D 0) #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1)) =20 -int read_file(const char *path, char *buf, size_t buflen); -int write_file(const char *path, const char *buf, size_t buflen); -int read_num(const char *path, unsigned long *num); -int write_num(const char *path, unsigned long num); - void shm_limits_prepare(unsigned long length); void __shm_limits_restore(void); =20 --=20 2.39.5 From nobody Mon Jun 8 17:39:50 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6ED9C33BBD7; Wed, 27 May 2026 14:25:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891924; cv=none; b=tJJiWK9ySSyfF3Wikn17Of1VL+Nb4BjASjdBHexXaCp1M1cQAq09Nbptjyi5I5czY/+LW+S6CHPopyiKX7spBK9Iim57UH7zVy2tgsJVyaAiN25svhw1ukiy3GwFK10CYR8/xS7GIasKMU8HpnaujEJChguh3fxCiSQPjmmtciI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891924; c=relaxed/simple; bh=LMQwBAzLorx7ZX97gFykhKpDRwlpqmjjMZB21wfH9Fg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GopEZmS22TRLel6LE+yeWvdeH4NCMo5YKho5rK/U3mjwEy8WxbquZqujzrSDcXV7R5fI2wp3QxXibx1Fo/bgKJGDtTS56C8xtsdQjJLgPgrF0HdeZzcmJp9mozpbMCAXglyTK77BqG72y35D0gjCdKTngSR3ybmiyFjThkfSWEg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=Ag0XuFWT; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="Ag0XuFWT" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BC79227B5; Wed, 27 May 2026 07:25:16 -0700 (PDT) Received: from a081061.blr.arm.com (a081061.arm.com [10.164.19.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 078DE3F905; Wed, 27 May 2026 07:25:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779891921; bh=LMQwBAzLorx7ZX97gFykhKpDRwlpqmjjMZB21wfH9Fg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ag0XuFWTM7SU88OssPsQBNd7xMoj7kuOcZJkN4l3y9mb9Lu1WxqVJ+J/9Ibd1E1Oc 4yFrrj/+SzIhq8Ze8AFfmdXOv5QmXnvrrqUPWHrFVdo+GKiHSmzwLQqqGa72cbx9Vi CWwBhmGUgXR50kJ7jBTf1wxkCgPSiiOmHVkvZZFc= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Jason Gunthorpe , John Hubbard , Peter Xu , Leon Romanovsky , Jonathan Corbet , Shuah Khan , Mark Brown , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Sarthak Sharma Subject: [PATCH v4 3/5] tools/lib/mm: move hugepage_settings out of selftests Date: Wed, 27 May 2026 19:54:30 +0530 Message-ID: <20260527142432.230127-4-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260527142432.230127-1-sarthak.sharma@arm.com> References: <20260527142432.230127-1-sarthak.sharma@arm.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" Move hugepage_settings.[ch] from tools/testing/selftests/mm/ to tools/lib/mm/ so the THP and HugeTLB helpers can be shared more easily between selftests and other tools. Keep the helpers exposed to mm selftests through vm_util.h where possible, and use direct includes for users that do not include vm_util.h. Adjust the selftests/mm build to compile the moved implementation from its new location. Remove the remaining kselftest dependency from hugepage_settings.c by replacing ksft_print_msg() calls with TAP comment style fprintf() diagnostics. Update the explicit x86 protection_keys 32-bit and 64-bit build rules to preserve prerequisite paths, now that hugepage_settings.c is built from tools/lib/mm/. Signed-off-by: Sarthak Sharma --- .../selftests =3D> lib}/mm/hugepage_settings.c | 18 +++++++++++++----- .../selftests =3D> lib}/mm/hugepage_settings.h | 0 tools/testing/selftests/mm/Makefile | 10 ++++++---- tools/testing/selftests/mm/compaction_test.c | 2 +- tools/testing/selftests/mm/cow.c | 1 - .../selftests/mm/folio_split_race_test.c | 1 - tools/testing/selftests/mm/guard-regions.c | 1 - tools/testing/selftests/mm/gup_longterm.c | 1 - tools/testing/selftests/mm/gup_test.c | 1 - tools/testing/selftests/mm/hmm-tests.c | 6 +++--- tools/testing/selftests/mm/hugetlb-madvise.c | 1 - tools/testing/selftests/mm/hugetlb-mmap.c | 1 - tools/testing/selftests/mm/hugetlb-mremap.c | 1 - tools/testing/selftests/mm/hugetlb-shm.c | 1 - .../selftests/mm/hugetlb-soft-offline.c | 2 +- tools/testing/selftests/mm/hugetlb-vmemmap.c | 1 - tools/testing/selftests/mm/hugetlb_dio.c | 1 - .../selftests/mm/hugetlb_fault_after_madv.c | 1 - .../testing/selftests/mm/hugetlb_madv_vs_map.c | 1 - tools/testing/selftests/mm/khugepaged.c | 1 - tools/testing/selftests/mm/ksm_tests.c | 1 - tools/testing/selftests/mm/migration.c | 5 ++--- tools/testing/selftests/mm/pagemap_ioctl.c | 1 - tools/testing/selftests/mm/prctl_thp_disable.c | 1 - tools/testing/selftests/mm/protection_keys.c | 2 +- tools/testing/selftests/mm/soft-dirty.c | 1 - .../selftests/mm/split_huge_page_test.c | 1 - tools/testing/selftests/mm/thuge-gen.c | 1 - tools/testing/selftests/mm/transhuge-stress.c | 1 - tools/testing/selftests/mm/uffd-common.h | 1 - tools/testing/selftests/mm/uffd-wp-mremap.c | 2 +- .../testing/selftests/mm/va_high_addr_switch.c | 1 - tools/testing/selftests/mm/vm_util.h | 1 + 33 files changed, 29 insertions(+), 42 deletions(-) rename tools/{testing/selftests =3D> lib}/mm/hugepage_settings.c (97%) rename tools/{testing/selftests =3D> lib}/mm/hugepage_settings.h (100%) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/= hugepage_settings.c similarity index 97% rename from tools/testing/selftests/mm/hugepage_settings.c rename to tools/lib/mm/hugepage_settings.c index c2f97fe97e58..c75b9c8f4a74 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/lib/mm/hugepage_settings.c @@ -10,11 +10,16 @@ #include #include =20 -#include "vm_util.h" +#include "file_utils.h" #include "hugepage_settings.h" =20 #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/" #define MAX_SETTINGS_DEPTH 4 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH]; static int settings_index; static struct thp_settings saved_settings; @@ -419,8 +424,8 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], in= t max) if (sscanf(entry->d_name, "hugepages-%zukB", &kb) !=3D 1) continue; sizes[count++] =3D kb * 1024; - ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n", - kb); + fprintf(stderr, "# [INFO] detected hugetlb page size: %zu KiB\n", + kb); } closedir(dir); return count; @@ -563,7 +568,8 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned = long sizes[], return 0; =20 if (nr_enabled > max) { - ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_en= abled, max); + fprintf(stderr, "# detected %d huge page sizes, will only test %d\n", + nr_enabled, max); nr_enabled =3D max; } =20 @@ -635,8 +641,10 @@ static void hugepage_restore_settings_atexit(void) =20 static void hugepage_restore_settings_sighandler(int sig) { + (void)sig; + /* exit() will invoke the hugepage_restore_settings_atexit handler. */ - exit(KSFT_FAIL); + exit(EXIT_FAILURE); } =20 void hugepage_save_settings(bool thp, bool hugetlb) diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/lib/mm/= hugepage_settings.h similarity index 100% rename from tools/testing/selftests/mm/hugepage_settings.h rename to tools/lib/mm/hugepage_settings.h diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/= mm/Makefile index b5fb4b6ab31b..f82db42a3823 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -187,8 +187,10 @@ TEST_FILES +=3D write_hugetlb_memory.sh =20 include ../lib.mk =20 -$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/m= m/file_utils.c -$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/m= m/file_utils.c +$(TEST_GEN_PROGS): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.= c \ + $(top_srcdir)/tools/lib/mm/file_utils.c +$(TEST_GEN_FILES): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.= c \ + $(top_srcdir)/tools/lib/mm/file_utils.c =20 $(OUTPUT)/uffd-stress: uffd-common.c $(OUTPUT)/uffd-unit-tests: uffd-common.c @@ -217,7 +219,7 @@ $(BINARIES_32): CFLAGS +=3D -m32 -mxsave $(BINARIES_32): LDLIBS +=3D -lrt -ldl -lm $(BINARIES_32): $(OUTPUT)/%_32: %.c $(call msg,CC,,$@) - $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $^ $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-32,$(t)))) endif =20 @@ -226,7 +228,7 @@ $(BINARIES_64): CFLAGS +=3D -m64 -mxsave $(BINARIES_64): LDLIBS +=3D -lrt -ldl $(BINARIES_64): $(OUTPUT)/%_64: %.c $(call msg,CC,,$@) - $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $^ $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-64,$(t)))) endif =20 diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/s= elftests/mm/compaction_test.c index 5b582588e015..b2eaa490e7c2 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -15,9 +15,9 @@ #include #include #include +#include =20 #include "kselftest.h" -#include "hugepage_settings.h" =20 #define MAP_SIZE_MB 100 #define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024) diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/= cow.c index 0c627ea89ff7..3c3d129c91b2 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -29,7 +29,6 @@ #include "../../../../mm/gup_test.h" #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 static size_t pagesize; static int pagemap_fd; diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/tes= ting/selftests/mm/folio_split_race_test.c index 6329e37fff4c..496b74b37476 100644 --- a/tools/testing/selftests/mm/folio_split_race_test.c +++ b/tools/testing/selftests/mm/folio_split_race_test.c @@ -25,7 +25,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 uint64_t page_size; uint64_t pmd_pagesize; diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/sel= ftests/mm/guard-regions.c index b21df3040b1c..ed967b3c7f37 100644 --- a/tools/testing/selftests/mm/guard-regions.c +++ b/tools/testing/selftests/mm/guard-regions.c @@ -21,7 +21,6 @@ #include #include #include "vm_util.h" -#include "hugepage_settings.h" =20 #include "../pidfd/pidfd.h" =20 diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/self= tests/mm/gup_longterm.c index eb8963e9d98f..68c840bae082 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -29,7 +29,6 @@ #include "../../../../mm/gup_test.h" #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 static size_t pagesize; static int nr_hugetlbsizes; diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftest= s/mm/gup_test.c index 3f841a96f870..5f44761dbec0 100644 --- a/tools/testing/selftests/mm/gup_test.c +++ b/tools/testing/selftests/mm/gup_test.c @@ -14,7 +14,6 @@ #include #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 #define MB (1UL << 20) =20 diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftes= ts/mm/hmm-tests.c index e1c8a679a4cf..b7f85a144f03 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -10,9 +10,6 @@ * bugs. */ =20 -#include "kselftest_harness.h" -#include "hugepage_settings.h" - #include #include #include @@ -27,6 +24,9 @@ #include #include #include +#include + +#include "kselftest_harness.h" =20 /* * This is a private UAPI to the kernel test module so it isn't exported diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/s= elftests/mm/hugetlb-madvise.c index 555b4b3d1430..57cf790ca478 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -14,7 +14,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #define MIN_FREE_PAGES 20 #define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */ diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/self= tests/mm/hugetlb-mmap.c index 0f2aad1b7dbd..a458becf5832 100644 --- a/tools/testing/selftests/mm/hugetlb-mmap.c +++ b/tools/testing/selftests/mm/hugetlb-mmap.c @@ -18,7 +18,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #define LENGTH (256UL*1024*1024) #define PROTECTION (PROT_READ | PROT_WRITE) diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/se= lftests/mm/hugetlb-mremap.c index ed3d92e862d8..9b724af66e93 100644 --- a/tools/testing/selftests/mm/hugetlb-mremap.c +++ b/tools/testing/selftests/mm/hugetlb-mremap.c @@ -26,7 +26,6 @@ #include #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 #define DEFAULT_LENGTH_MB 10UL #define MB_TO_BYTES(x) (x * 1024 * 1024) diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selft= ests/mm/hugetlb-shm.c index 3ff7f062b7eb..f4514da49e1d 100644 --- a/tools/testing/selftests/mm/hugetlb-shm.c +++ b/tools/testing/selftests/mm/hugetlb-shm.c @@ -29,7 +29,6 @@ #include =20 #include "vm_util.h" -#include "hugepage_settings.h" =20 #define LENGTH (256UL*1024*1024) =20 diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/test= ing/selftests/mm/hugetlb-soft-offline.c index bc202e4ed2bd..20864e7d4e0c 100644 --- a/tools/testing/selftests/mm/hugetlb-soft-offline.c +++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c @@ -21,9 +21,9 @@ #include #include #include +#include =20 #include "kselftest.h" -#include "hugepage_settings.h" =20 #ifndef MADV_SOFT_OFFLINE #define MADV_SOFT_OFFLINE 101 diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/s= elftests/mm/hugetlb-vmemmap.c index 507df78a158d..c46a656c25a0 100644 --- a/tools/testing/selftests/mm/hugetlb-vmemmap.c +++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c @@ -11,7 +11,6 @@ #include #include #include "vm_util.h" -#include "hugepage_settings.h" =20 #define PAGE_COMPOUND_HEAD (1UL << 15) #define PAGE_COMPOUND_TAIL (1UL << 16) diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selft= ests/mm/hugetlb_dio.c index fb4600570e13..9495974eccbe 100644 --- a/tools/testing/selftests/mm/hugetlb_dio.c +++ b/tools/testing/selftests/mm/hugetlb_dio.c @@ -20,7 +20,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #ifndef STATX_DIOALIGN #define STATX_DIOALIGN 0x00002000U diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/= testing/selftests/mm/hugetlb_fault_after_madv.c index 2dc158054f66..56c5a8533e9d 100644 --- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c +++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c @@ -10,7 +10,6 @@ =20 #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #define INLOOP_ITER 100 =20 diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testi= ng/selftests/mm/hugetlb_madv_vs_map.c index f94549efcc6f..2532a42b98df 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -25,7 +25,6 @@ #include =20 #include "vm_util.h" -#include "hugepage_settings.h" =20 #define INLOOP_ITER 100 =20 diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selfte= sts/mm/khugepaged.c index bc923148e983..724e3f93be15 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -22,7 +22,6 @@ #include "linux/magic.h" =20 #include "vm_util.h" -#include "hugepage_settings.h" =20 #define BASE_ADDR ((void *)(1UL << 30)) static unsigned long hpage_pmd_size; diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftes= ts/mm/ksm_tests.c index a050f4840cfa..cc37492cf32c 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -15,7 +15,6 @@ #include "kselftest.h" #include #include "vm_util.h" -#include "hugepage_settings.h" =20 #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" #define KSM_FP(s) (KSM_SYSFS_PATH s) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftes= ts/mm/migration.c index 29f7492453d4..4fe97c033261 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -4,9 +4,6 @@ * paths in the kernel. */ =20 -#include "kselftest_harness.h" -#include "hugepage_settings.h" - #include #include #include @@ -16,6 +13,8 @@ #include #include #include + +#include "kselftest_harness.h" #include "vm_util.h" =20 #define TWOMEG (2<<20) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/sel= ftests/mm/pagemap_ioctl.c index 762306177ad8..510bd460ccd0 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -23,7 +23,6 @@ =20 #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \ PAGE_IS_FILE | PAGE_IS_PRESENT | \ diff --git a/tools/testing/selftests/mm/prctl_thp_disable.c b/tools/testing= /selftests/mm/prctl_thp_disable.c index d8d9d1de57b8..a4f8451791cb 100644 --- a/tools/testing/selftests/mm/prctl_thp_disable.c +++ b/tools/testing/selftests/mm/prctl_thp_disable.c @@ -14,7 +14,6 @@ #include =20 #include "kselftest_harness.h" -#include "hugepage_settings.h" #include "vm_util.h" =20 #ifndef PR_THP_DISABLE_EXCEPT_ADVISED diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/s= elftests/mm/protection_keys.c index 9a6d954ee371..5ba2033e8a09 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -45,8 +45,8 @@ #include #include #include +#include =20 -#include "hugepage_settings.h" #include "pkey-helpers.h" =20 int iteration_nr =3D 1; diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selfte= sts/mm/soft-dirty.c index fb1864a68e1c..6c22ac9e93db 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -9,7 +9,6 @@ =20 #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 #define PAGEMAP_FILE_PATH "/proc/self/pagemap" #define TEST_ITERATIONS 10000 diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/test= ing/selftests/mm/split_huge_page_test.c index 98236db7c813..d959089b3dfb 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -21,7 +21,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 uint64_t pagesize; unsigned int pageshift; diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftes= ts/mm/thuge-gen.c index 22b9c2f1c35d..0da15d530a1f 100644 --- a/tools/testing/selftests/mm/thuge-gen.c +++ b/tools/testing/selftests/mm/thuge-gen.c @@ -14,7 +14,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 #if !defined(MAP_HUGETLB) #define MAP_HUGETLB 0x40000 diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/= selftests/mm/transhuge-stress.c index 8eb0c5630e7e..96f72898ebe0 100644 --- a/tools/testing/selftests/mm/transhuge-stress.c +++ b/tools/testing/selftests/mm/transhuge-stress.c @@ -17,7 +17,6 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 int backing_fd =3D -1; int mmap_flags =3D MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE; diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selft= ests/mm/uffd-common.h index 92a21b97f745..0723843a7626 100644 --- a/tools/testing/selftests/mm/uffd-common.h +++ b/tools/testing/selftests/mm/uffd-common.h @@ -37,7 +37,6 @@ =20 #include "kselftest.h" #include "vm_util.h" -#include "hugepage_settings.h" =20 #define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY) =20 diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/se= lftests/mm/uffd-wp-mremap.c index c973d6722720..eb4b2433b00e 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -7,8 +7,8 @@ #include #include #include +#include #include "kselftest.h" -#include "hugepage_settings.h" #include "uffd-common.h" =20 static int pagemap_fd; diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testi= ng/selftests/mm/va_high_addr_switch.c index e24d7ba00b44..5a354a664d1f 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -11,7 +11,6 @@ =20 #include "vm_util.h" #include "kselftest.h" -#include "hugepage_settings.h" =20 /* * The hint addr value is used to allocate addresses diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests= /mm/vm_util.h index d45135283732..aa856f0d32d6 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -9,6 +9,7 @@ #include "kselftest.h" #include #include +#include =20 #define BIT_ULL(nr) (1ULL << (nr)) #define PM_SOFT_DIRTY BIT_ULL(55) --=20 2.39.5 From nobody Mon Jun 8 17:39:50 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3E09D242D9B; Wed, 27 May 2026 14:25:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891936; cv=none; b=UeNsvwLhEIE85znq5CjcE/ov5OorP/VZEA/uBIyF+Jo5TmCbYGzs/q+evxcYxKpiJUa5qp9Kdoe+HgHbfoXzi4HVV5ULbFjOB64RFkoQ1VMdENnsQMNBf/EwF2wPS29BqEO6nU+mSgrY3NBi0Ul9z9yTCqL5AwqxErqyvpO7FCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891936; c=relaxed/simple; bh=ukUyIa/lSr5zxWMkQop+vroAz1QmgjIK6x8ZvMDYGAA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lqtDN2xw2F57V9DkiZ+p2RJWXEJALPJYY/F+33K8Y50zSljWStYsBWqXKXLgYqOfK7bKiejV5rRy6aGnPimBRn5YgHs7CMBcNVeLydEsBh5drMVB6+2kjQoSjcnvaFDQxsTcsi2nc3i18fkP2n9/o6tOTmGHvIhD5OpRxrsJDZM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=e0/VrrN3; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="e0/VrrN3" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id DC87727DC; Wed, 27 May 2026 07:25:26 -0700 (PDT) Received: from a081061.blr.arm.com (a081061.arm.com [10.164.19.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 7CD223F905; Wed, 27 May 2026 07:25:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779891931; bh=ukUyIa/lSr5zxWMkQop+vroAz1QmgjIK6x8ZvMDYGAA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e0/VrrN3pSOjUoBCAxskSUU2iEpS71XbQH64Eo6ItPrn4E9mdTwe/QuXvK6sNrm9N SE+aMy3InVifTZIxhXHyW89tdKjcVNWKF0gs3h1+E+tKj25rCGscHvyPgTintKMphp KbgcsjbbMFuX/2tmmAflWlmJNK78YvvjKJDgR1Nc= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Jason Gunthorpe , John Hubbard , Peter Xu , Leon Romanovsky , Jonathan Corbet , Shuah Khan , Mark Brown , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Sarthak Sharma Subject: [PATCH v4 4/5] tools/mm: add a standalone GUP microbenchmark Date: Wed, 27 May 2026 19:54:31 +0530 Message-ID: <20260527142432.230127-5-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260527142432.230127-1-sarthak.sharma@arm.com> References: <20260527142432.230127-1-sarthak.sharma@arm.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" Add a command-line tool for benchmarking get_user_pages fast-path (GUP_FAST), pin_user_pages fast-path (PIN_FAST), and pin_user_pages longterm (PIN_LONGTERM) via the CONFIG_GUP_TEST debugfs interface. When invoked without arguments, gup_bench runs the same matrix of configurations as run_gup_matrix() in run_vmtests.sh: all three GUP commands across read/write, private/shared mappings, and a range of page counts, with THP on/off for regular mappings and hugetlb for huge page mappings. Restore HugeTLB settings after each hugetlb benchmark run. Validate numeric command-line arguments instead of relying on atoi(). This tool is a mix of reused and new logic. The mapping/setup path comes from selftests/mm/gup_test.c, while the default benchmark matrix matches run_gup_matrix() in run_vmtests.sh. The standalone CLI and tools/mm integration are added here and the HugeTLB setup code is shared via tools/lib/mm. Add gup_bench to BUILD_TARGETS and INSTALL_TARGETS in tools/mm/Makefile, link it against the shared tools/lib/mm hugepage helpers, and ignore the resulting binary in tools/mm/.gitignore. While here, also add the missing thp_swap_allocator_test entry to .gitignore. Add tools/mm/gup_bench.c to the GUP entry in MAINTAINERS. Suggested-by: David Hildenbrand (Arm) Signed-off-by: Sarthak Sharma --- MAINTAINERS | 1 + tools/mm/.gitignore | 2 + tools/mm/Makefile | 10 +- tools/mm/gup_bench.c | 433 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 443 insertions(+), 3 deletions(-) create mode 100644 tools/mm/gup_bench.c diff --git a/MAINTAINERS b/MAINTAINERS index 5d2f72bbd128..402b4de7b7e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16839,6 +16839,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/gi= t/akpm/mm F: mm/gup.c F: mm/gup_test.c F: mm/gup_test.h +F: tools/mm/gup_bench.c F: tools/testing/selftests/mm/gup_longterm.c F: tools/testing/selftests/mm/gup_test.c =20 diff --git a/tools/mm/.gitignore b/tools/mm/.gitignore index 922879f93fc8..154d740be02e 100644 --- a/tools/mm/.gitignore +++ b/tools/mm/.gitignore @@ -2,3 +2,5 @@ slabinfo page-types page_owner_sort +thp_swap_allocator_test +gup_bench diff --git a/tools/mm/Makefile b/tools/mm/Makefile index f5725b5c23aa..d82cc8c43ee0 100644 --- a/tools/mm/Makefile +++ b/tools/mm/Makefile @@ -3,13 +3,14 @@ # include ../scripts/Makefile.include =20 -BUILD_TARGETS=3Dpage-types slabinfo page_owner_sort thp_swap_allocator_test +BUILD_TARGETS=3Dpage-types slabinfo page_owner_sort thp_swap_allocator_tes= t gup_bench INSTALL_TARGETS =3D $(BUILD_TARGETS) thpmaps =20 LIB_DIR =3D ../lib/api LIBS =3D $(LIB_DIR)/libapi.a +GUP_BENCH_OBJS =3D gup_bench.c ../lib/mm/hugepage_settings.c ../lib/mm/fil= e_utils.c =20 -CFLAGS +=3D -Wall -Wextra -I../lib/ -pthread +CFLAGS +=3D -Wall -Wextra -I../lib/ -I../.. -pthread LDFLAGS +=3D $(LIBS) -pthread =20 all: $(BUILD_TARGETS) @@ -22,8 +23,11 @@ $(LIBS): %: %.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) =20 +gup_bench: $(GUP_BENCH_OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(GUP_BENCH_OBJS) $(LDFLAGS) + clean: - $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test + $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test gup_ben= ch make -C $(LIB_DIR) clean =20 sbindir ?=3D /usr/sbin diff --git a/tools/mm/gup_bench.c b/tools/mm/gup_bench.c new file mode 100644 index 000000000000..b3990fd7c1d9 --- /dev/null +++ b/tools/mm/gup_bench.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microbenchmark for get_user_pages (GUP) kernel interfaces. + * + * Exercises GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK, and + * PIN_LONGTERM_BENCHMARK via the CONFIG_GUP_TEST debugfs interface. + * + * Example use: + * # Run the full matrix (all commands, access modes, page counts): + * ./gup_bench + * + * # Single run: pin_user_pages_fast, 512 pages, write access, hugetlb: + * ./gup_bench -a -n 512 -w -H + * + * Requires CONFIG_GUP_TEST=3Dy and debugfs mounted at /sys/kernel/debug. + * Must be run as root. + */ + +#define __SANE_USERSPACE_TYPES__ // Use ll64 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MB (1UL << 20) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +/* Just the flags we need, copied from the kernel internals. */ +#define FOLL_WRITE 0x01 /* check pte is writable */ + +#define GUP_TEST_FILE "/sys/kernel/debug/gup_test" + +static unsigned int psize(void) +{ + static unsigned int __page_size; + + if (!__page_size) + __page_size =3D sysconf(_SC_PAGESIZE); + return __page_size; +} + +static unsigned long cmd; +static const char *bench_label; +static int gup_fd, repeats =3D 1; +static unsigned long size =3D 128 * MB; +static atomic_int bench_error; +/* Serialize prints */ +static pthread_mutex_t print_mutex =3D PTHREAD_MUTEX_INITIALIZER; + +static const unsigned long bench_cmds[] =3D { + GUP_FAST_BENCHMARK, + PIN_FAST_BENCHMARK, + PIN_LONGTERM_BENCHMARK, +}; + +static const int bench_thp_modes[] =3D { 1, 0 }; /* on, off */ +static const int bench_nr_pages_list[] =3D { 1, 512, 123, -1 }; + +static const char *cmd_to_str(unsigned long cmd) +{ + switch (cmd) { + case GUP_FAST_BENCHMARK: + return "GUP_FAST_BENCHMARK"; + case PIN_FAST_BENCHMARK: + return "PIN_FAST_BENCHMARK"; + case PIN_LONGTERM_BENCHMARK: + return "PIN_LONGTERM_BENCHMARK"; + } + return "Unknown command"; +} + +static long parse_long_arg(const char *arg, const char *name) +{ + char *end; + long val; + + errno =3D 0; + val =3D strtol(arg, &end, 10); + if (errno || end =3D=3D arg || *end !=3D '\0') { + fprintf(stderr, "Invalid %s '%s'\n", name, arg); + exit(1); + } + + return val; +} + +static long parse_positive_long_arg(const char *arg, const char *name) +{ + long val =3D parse_long_arg(arg, name); + + if (val < 1) { + fprintf(stderr, "Invalid %s '%s'\n", name, arg); + exit(1); + } + + return val; +} + +struct bench_run { + unsigned long cmd; + int thp; /* -1: default, 0: off, 1: on */ + bool hugetlb; + bool write; + bool shared; + int nr_pages; /* -1 means all pages (size / psize()) */ + unsigned long size; + char *file; + int nthreads; + unsigned int gup_flags; +}; + +void *gup_thread(void *data) +{ + struct gup_test gup =3D *(struct gup_test *)data; + int i, status; + + for (i =3D 0; i < repeats; i++) { + gup.size =3D size; + status =3D ioctl(gup_fd, cmd, &gup); + if (status) { + bench_error =3D 1; + break; + } + + pthread_mutex_lock(&print_mutex); + printf("%s time: get:%lld put:%lld us", + bench_label, gup.get_delta_usec, + gup.put_delta_usec); + if (gup.size !=3D size) + printf(", truncated (size: %lld)", gup.size); + printf("\n"); + pthread_mutex_unlock(&print_mutex); + } + + return NULL; +} + +static int run_bench(struct bench_run *run) +{ + struct gup_test gup =3D { 0 }; + int zero_fd, i, ret, started_threads =3D 0; + int flags =3D MAP_PRIVATE; + pthread_t *tid; + char label[128]; + char *p; + bool restore_hugetlb =3D false; + + /* Set globals consumed by gup_thread */ + cmd =3D run->cmd; + size =3D run->size; + bench_error =3D 0; + + if (run->hugetlb) { + unsigned long hp_size =3D default_huge_page_size(); + + if (!hp_size) { + fprintf(stderr, "Could not determine huge page size\n"); + return 1; + } + size =3D (size + hp_size - 1) & ~(hp_size - 1); + if (!hugetlb_setup_default(size / hp_size)) { + fprintf(stderr, "Not enough huge pages\n"); + hugetlb_restore_settings(); + return 1; + } + restore_hugetlb =3D true; + flags |=3D (MAP_HUGETLB | MAP_ANONYMOUS); + } + + if (run->shared) { + flags &=3D ~MAP_PRIVATE; + flags |=3D MAP_SHARED; + } + + gup.nr_pages_per_call =3D run->nr_pages < 0 ? size / psize() : + (unsigned long)run->nr_pages; + + gup.gup_flags =3D run->gup_flags; + if (run->write) + gup.gup_flags |=3D FOLL_WRITE; + + snprintf(label, sizeof(label), "%s (nr_pages=3D%-4u %s %s %s %s)", + cmd_to_str(run->cmd), + gup.nr_pages_per_call, + run->write ? "write" : "read", + run->shared ? "shared" : "private", + run->hugetlb ? "hugetlb=3Don" : "hugetlb=3Doff", + run->hugetlb ? "thp=3Doff" : + (run->thp =3D=3D 1 ? "thp=3Don" : + (run->thp =3D=3D 0 ? "thp=3Doff" : "thp=3Ddefault"))); + bench_label =3D label; + + zero_fd =3D open(run->file, O_RDWR); + if (zero_fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", run->file, strerror(errno)); + if (restore_hugetlb) + hugetlb_restore_settings(); + return 1; + } + + p =3D mmap(NULL, size, PROT_READ | PROT_WRITE, flags, zero_fd, 0); + close(zero_fd); + if (p =3D=3D MAP_FAILED) { + fprintf(stderr, "mmap: %s\n", strerror(errno)); + if (restore_hugetlb) + hugetlb_restore_settings(); + return 1; + } + gup.addr =3D (unsigned long)p; + + if (run->thp =3D=3D 1) + madvise(p, size, MADV_HUGEPAGE); + else if (run->thp =3D=3D 0) + madvise(p, size, MADV_NOHUGEPAGE); + + /* Fault them in here, from user space. */ + for (; (unsigned long)p < gup.addr + size; p +=3D psize()) + p[0] =3D 0; + + tid =3D malloc(sizeof(pthread_t) * run->nthreads); + if (!tid) { + fprintf(stderr, "Failed to allocate %d threads: %s\n", + run->nthreads, strerror(errno)); + munmap((void *)gup.addr, size); + if (restore_hugetlb) + hugetlb_restore_settings(); + return 1; + } + + for (i =3D 0; i < run->nthreads; i++) { + ret =3D pthread_create(&tid[i], NULL, gup_thread, &gup); + if (ret) { + fprintf(stderr, "pthread_create failed: %s\n", strerror(ret)); + bench_error =3D 1; + break; + } + started_threads++; + } + for (i =3D 0; i < started_threads; i++) { + ret =3D pthread_join(tid[i], NULL); + if (ret) { + fprintf(stderr, "pthread_join failed: %s\n", strerror(ret)); + bench_error =3D 1; + } + } + + free(tid); + munmap((void *)gup.addr, size); + if (restore_hugetlb) + hugetlb_restore_settings(); + + return bench_error ? 1 : 0; +} + +static int run_matrix(void) +{ + unsigned int c, t, w, s, n; + int ret =3D 0; + + for (c =3D 0; c < ARRAY_SIZE(bench_cmds); c++) { + for (w =3D 0; w <=3D 1; w++) { + for (s =3D 0; s <=3D 1; s++) { + for (t =3D 0; t < ARRAY_SIZE(bench_thp_modes); t++) { + for (n =3D 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) { + struct bench_run run =3D { + .cmd =3D bench_cmds[c], + .thp =3D bench_thp_modes[t], + .hugetlb =3D false, + .write =3D w, + .shared =3D s, + .nr_pages =3D bench_nr_pages_list[n], + .size =3D 128 * MB, + .file =3D "/dev/zero", + .nthreads =3D 1, + }; + ret |=3D run_bench(&run); + } + } + /* hugetlb: 256M to match run_gup_matrix() in run_vmtests.sh */ + for (n =3D 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) { + struct bench_run run =3D { + .cmd =3D bench_cmds[c], + .thp =3D -1, + .hugetlb =3D true, + .write =3D w, + .shared =3D s, + .nr_pages =3D bench_nr_pages_list[n], + .size =3D 256 * MB, + .file =3D "/dev/zero", + .nthreads =3D 1, + }; + ret |=3D run_bench(&run); + } + } + } + } + return ret; +} + +int main(int argc, char **argv) +{ + struct bench_run run =3D { + .cmd =3D GUP_FAST_BENCHMARK, + .thp =3D -1, + .hugetlb =3D false, + .write =3D true, + .shared =3D false, + .nr_pages =3D 1, + .size =3D 128 * MB, + .file =3D "/dev/zero", + .nthreads =3D 1, + }; + int opt, result; + + while ((opt =3D getopt(argc, argv, "m:r:n:F:f:aj:tTLuwWSH")) !=3D -1) { + switch (opt) { + /* Command selection */ + case 'u': + run.cmd =3D GUP_FAST_BENCHMARK; + break; + case 'a': + run.cmd =3D PIN_FAST_BENCHMARK; + break; + case 'L': + run.cmd =3D PIN_LONGTERM_BENCHMARK; + break; + + /* Memory type */ + case 'H': + run.hugetlb =3D true; + break; + case 't': + run.thp =3D 1; + break; + case 'T': + run.thp =3D 0; + break; + + /* Access mode */ + case 'w': + run.write =3D true; + break; + case 'W': + run.write =3D false; + break; + case 'S': + run.shared =3D true; + break; + + /* Mapping */ + case 'f': + run.file =3D optarg; + break; + + /* Sizing and iteration */ + case 'm': + run.size =3D parse_positive_long_arg(optarg, "size") * MB; + break; + case 'n': + run.nr_pages =3D parse_long_arg(optarg, "page count"); + if (run.nr_pages < -1) { + fprintf(stderr, "Invalid page count '%s'\n", optarg); + exit(1); + } + break; + case 'r': + repeats =3D parse_positive_long_arg(optarg, "repeat count"); + break; + case 'j': { + long val; + + val =3D parse_positive_long_arg(optarg, "thread count"); + if (val > INT_MAX || + (size_t)val > SIZE_MAX / sizeof(pthread_t)) { + fprintf(stderr, "Invalid thread count '%s'\n", optarg); + exit(1); + } + run.nthreads =3D val; + break; + } + + /* Advanced */ + case 'F': + /* strtol, so you can pass flags in hex form */ + run.gup_flags =3D strtol(optarg, 0, 0); + break; + + default: + fprintf(stderr, "Wrong argument\n"); + exit(1); + } + } + + gup_fd =3D open(GUP_TEST_FILE, O_RDWR); + if (gup_fd =3D=3D -1) { + if (errno =3D=3D EACCES) { + fprintf(stderr, "Please run as root\n"); + } else if (errno =3D=3D ENOENT) { + DIR *debugfs =3D opendir("/sys/kernel/debug"); + + if (!debugfs) { + fprintf(stderr, "Mount debugfs at /sys/kernel/debug\n"); + } else { + closedir(debugfs); + fprintf(stderr, "Check CONFIG_GUP_TEST in kernel config\n"); + } + } else { + fprintf(stderr, "Failed to open %s: %s\n", GUP_TEST_FILE, + strerror(errno)); + } + exit(1); + } + + result =3D (argc =3D=3D 1) ? run_matrix() : run_bench(&run); + close(gup_fd); + return result; +} --=20 2.39.5 From nobody Mon Jun 8 17:39:50 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9B63141B366; Wed, 27 May 2026 14:25:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891955; cv=none; b=Xrc773RiSpFDC1YPnLTXWvHRSzywjPQM378a7zeNf69SuKGtq4G7V0SFwoZDfQ89I2fWNkVtMcwliSz5AUOzCVQzFh8TQRAcPWbYc+xbjJ8AhbORiiwn43gndjXN+8/HrHamwh9Fm2B1ADMoh0zCwdGvaa9ONfd8Toq7gkLaGc4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779891955; c=relaxed/simple; bh=VI/X+Azo6J3U3/1Rte7OmACVQHQsrGhZhEsHT21O96c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=id2I2/SJ8E4DGPwTRU4eTQg9z5Q9aPzXKxiYpxZ47It22YmiOvIVXzI312NlcSq9wPf9eObGbNBuqxJJHp0xblF+9k8ksAefuk3IEbngvSB1EHZaCZpLN4b3eJ07WRaGd3scNcWvBw+0ofYxc0RYVFLK3nLK6m9Gdn9PvohuTp4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=Tx43fLtf; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="Tx43fLtf" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 10451358F; Wed, 27 May 2026 07:25:37 -0700 (PDT) Received: from a081061.blr.arm.com (a081061.arm.com [10.164.19.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 9EB7F3F905; Wed, 27 May 2026 07:25:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779891942; bh=VI/X+Azo6J3U3/1Rte7OmACVQHQsrGhZhEsHT21O96c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Tx43fLtfe5MFvhiR0UnFe89HZ/ZhaXZ2H1mEhkyVetD/1WAPsMg5nr8GbccT6vcJQ CvO27r2nvxpZw3+TUj5zcGQgAIwwFkwBFcikEDr8hEuhdJLRq8gSNcfhgvOAlSJ+Yj S1YcuwNJxv5bDWzbbLiOlTF9bFLq5TzlK5KfF4ww= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Jason Gunthorpe , John Hubbard , Peter Xu , Leon Romanovsky , Jonathan Corbet , Shuah Khan , Mark Brown , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Sarthak Sharma Subject: [PATCH v4 5/5] selftests/mm: rewrite gup_test as a standalone harness-based selftest Date: Wed, 27 May 2026 19:54:32 +0530 Message-ID: <20260527142432.230127-6-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260527142432.230127-1-sarthak.sharma@arm.com> References: <20260527142432.230127-1-sarthak.sharma@arm.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" Rewrite gup_test.c using kselftest_harness.h. The new test covers 12 mapping configurations: THP on, THP off and hugetlb, each across private/shared and read/write variants. It runs seven test cases per variant: get_user_pages, get_user_pages_fast, pin_user_pages, pin_user_pages_fast, pin_user_pages_longterm, and DUMP_USER_PAGES_TEST via both get and pin. Each test case sweeps four nr_pages_per_call values: 1, 512, 123, and all pages. This preserves the old run_gup_matrix() sweep: 12 mapping combinations x 5 GUP/PUP operations x 4 batch sizes =3D 240 ioctl sweeps. It also expands DUMP_USER_PAGES_TEST coverage from one standalone invocation to 12 variants x 2 dump modes x 4 batch sizes =3D 96 additional sweeps. Preserve the old sparse dump coverage from run_vmtests.sh with one standalone test that exercises DUMP_USER_PAGES_TEST with page indices 0, 19 and 0x1000, fetching the full mapping in one ioctl call. This brings the total to 85 TAP-reported cases (12 x 7 + 1) and 337 ioctl sweeps (240 + 96 + 1). On a Radxa Orion O6 board, ./gup_test completes in 5.39s on average over 10 runs (range: 5.33s - 5.48s). Update run_vmtests.sh: remove run_gup_matrix() and the multiple flagged invocations of gup_test, replacing them with a single unconditional invocation. Benchmark functionality is handled by tools/mm/gup_bench introduced in the previous patch. Update Documentation/core-api/pin_user_pages.rst to reflect the new harness-based gup_test interface rather than command-line flag invocations. Suggested-by: David Hildenbrand (Arm) Signed-off-by: Sarthak Sharma --- Documentation/core-api/pin_user_pages.rst | 12 +- tools/testing/selftests/mm/gup_test.c | 605 ++++++++++++++-------- tools/testing/selftests/mm/run_vmtests.sh | 37 +- 3 files changed, 397 insertions(+), 257 deletions(-) diff --git a/Documentation/core-api/pin_user_pages.rst b/Documentation/core= -api/pin_user_pages.rst index c16ca163b55e..ea722adf22cc 100644 --- a/Documentation/core-api/pin_user_pages.rst +++ b/Documentation/core-api/pin_user_pages.rst @@ -230,10 +230,16 @@ This file:: =20 tools/testing/selftests/mm/gup_test.c =20 -has the following new calls to exercise the new pin*() wrapper functions: +contains the following test cases to exercise pin_user_pages*(): =20 -* PIN_FAST_BENCHMARK (./gup_test -a) -* PIN_BASIC_TEST (./gup_test -b) +* pin_user_pages via PIN_BASIC_TEST +* pin_user_pages_fast via PIN_FAST_BENCHMARK +* pin_user_pages_longterm via PIN_LONGTERM_BENCHMARK + +Run with:: + + make -C tools/testing/selftests/mm + ./tools/testing/selftests/mm/gup_test =20 You can monitor how many total dma-pinned pages have been acquired and rel= eased since the system was booted, via two new /proc/vmstat entries: :: diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftest= s/mm/gup_test.c index 5f44761dbec0..d9caa9fb8b78 100644 --- a/tools/testing/selftests/mm/gup_test.c +++ b/tools/testing/selftests/mm/gup_test.c @@ -1,274 +1,443 @@ #define __SANE_USERSPACE_TYPES__ // Use ll64 #include #include +#include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include "kselftest.h" #include "vm_util.h" +#include "kselftest_harness.h" =20 #define MB (1UL << 20) =20 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + /* Just the flags we need, copied from the kernel internals. */ #define FOLL_WRITE 0x01 /* check pte is writable */ =20 +/* Page counts exercising single, THP-batch, partial, and full-mapping GUP= . */ +static const int nr_pages_list[] =3D { 1, 512, 123, -1 }; + #define GUP_TEST_FILE "/sys/kernel/debug/gup_test" =20 -static unsigned long cmd =3D GUP_FAST_BENCHMARK; -static int gup_fd, repeats =3D 1; -static unsigned long size =3D 128 * MB; -/* Serialize prints */ -static pthread_mutex_t print_mutex =3D PTHREAD_MUTEX_INITIALIZER; +FIXTURE(gup_test) +{ + int gup_fd; + char *addr; + unsigned long size; +}; + +FIXTURE_VARIANT(gup_test) +{ + bool thp; + bool hugetlb; + bool write; + bool shared; +}; + +FIXTURE_VARIANT_ADD(gup_test, private_write) +{ + .thp =3D false, + .hugetlb =3D false, + .write =3D true, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, private_readonly) +{ + .thp =3D false, + .hugetlb =3D false, + .write =3D false, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, private_write_thp) +{ + .thp =3D true, + .hugetlb =3D false, + .write =3D true, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, private_readonly_thp) +{ + .thp =3D true, + .hugetlb =3D false, + .write =3D false, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, private_write_hugetlb) +{ + .thp =3D false, + .hugetlb =3D true, + .write =3D true, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, private_readonly_hugetlb) +{ + .thp =3D false, + .hugetlb =3D true, + .write =3D false, + .shared =3D false, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_write) +{ + .thp =3D false, + .hugetlb =3D false, + .write =3D true, + .shared =3D true, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_readonly) +{ + .thp =3D false, + .hugetlb =3D false, + .write =3D false, + .shared =3D true, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_write_thp) +{ + .thp =3D true, + .hugetlb =3D false, + .write =3D true, + .shared =3D true, +}; =20 -static char *cmd_to_str(unsigned long cmd) +FIXTURE_VARIANT_ADD(gup_test, shared_readonly_thp) { - switch (cmd) { - case GUP_FAST_BENCHMARK: - return "GUP_FAST_BENCHMARK"; - case PIN_FAST_BENCHMARK: - return "PIN_FAST_BENCHMARK"; - case PIN_LONGTERM_BENCHMARK: - return "PIN_LONGTERM_BENCHMARK"; - case GUP_BASIC_TEST: - return "GUP_BASIC_TEST"; - case PIN_BASIC_TEST: - return "PIN_BASIC_TEST"; - case DUMP_USER_PAGES_TEST: - return "DUMP_USER_PAGES_TEST"; + .thp =3D true, + .hugetlb =3D false, + .write =3D false, + .shared =3D true, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_write_hugetlb) +{ + .thp =3D false, + .hugetlb =3D true, + .write =3D true, + .shared =3D true, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_readonly_hugetlb) +{ + .thp =3D false, + .hugetlb =3D true, + .write =3D false, + .shared =3D true, +}; + +FIXTURE_SETUP(gup_test) +{ + int mmap_flags =3D MAP_PRIVATE; + int zero_fd; + char *p; + + /* zero_fd has to be >=3D 0. Already checked in main() */ + zero_fd =3D open("/dev/zero", O_RDWR); + ASSERT_GE(zero_fd, 0); + + /* gup_fd has to be >=3D 0. Already checked in main() */ + self->gup_fd =3D open(GUP_TEST_FILE, O_RDWR); + ASSERT_GE(self->gup_fd, 0); + + self->size =3D variant->hugetlb ? 256 * MB : 128 * MB; + + /* Check for hugetlb */ + if (variant->hugetlb) { + unsigned long hp_size =3D default_huge_page_size(); + + if (!hp_size) { + close(zero_fd); + close(self->gup_fd); + SKIP(return, "HugeTLB not available\n"); + } + + self->size =3D (self->size + hp_size - 1) & ~(hp_size - 1); + if (!hugetlb_setup_default(self->size / hp_size)) { + hugetlb_restore_settings(); + close(zero_fd); + close(self->gup_fd); + SKIP(return, "Not enough huge pages\n"); + } + + mmap_flags |=3D (MAP_HUGETLB | MAP_ANONYMOUS); + } + + if (variant->shared) + mmap_flags =3D (mmap_flags & ~MAP_PRIVATE) | MAP_SHARED; + + self->addr =3D mmap(NULL, self->size, PROT_READ | PROT_WRITE, + mmap_flags, zero_fd, 0); + + close(zero_fd); + + ASSERT_NE(self->addr, MAP_FAILED) { + int err =3D errno; + + close(self->gup_fd); + if (variant->hugetlb) + hugetlb_restore_settings(); + TH_LOG("mmap failed: %s", strerror(err)); } - return "Unknown command"; + + if (variant->thp) + madvise(self->addr, self->size, MADV_HUGEPAGE); + else + madvise(self->addr, self->size, MADV_NOHUGEPAGE); + + for (p =3D self->addr; (unsigned long)p < (unsigned long)self->addr + + self->size; p +=3D psize()) + p[0] =3D 0; } =20 -void *gup_thread(void *data) +FIXTURE_TEARDOWN(gup_test) { - struct gup_test gup =3D *(struct gup_test *)data; - int i, status; - - /* Only report timing information on the *_BENCHMARK commands: */ - if ((cmd =3D=3D PIN_FAST_BENCHMARK) || (cmd =3D=3D GUP_FAST_BENCHMARK) || - (cmd =3D=3D PIN_LONGTERM_BENCHMARK)) { - for (i =3D 0; i < repeats; i++) { - gup.size =3D size; - status =3D ioctl(gup_fd, cmd, &gup); - if (status) - break; - - pthread_mutex_lock(&print_mutex); - ksft_print_msg("%s: Time: get:%lld put:%lld us", - cmd_to_str(cmd), gup.get_delta_usec, - gup.put_delta_usec); - if (gup.size !=3D size) - ksft_print_msg(", truncated (size: %lld)", gup.size); - ksft_print_msg("\n"); - pthread_mutex_unlock(&print_mutex); - } - } else { - gup.size =3D size; - status =3D ioctl(gup_fd, cmd, &gup); - if (status) - goto return_; - - pthread_mutex_lock(&print_mutex); - ksft_print_msg("%s: done\n", cmd_to_str(cmd)); - if (gup.size !=3D size) - ksft_print_msg("Truncated (size: %lld)\n", gup.size); - pthread_mutex_unlock(&print_mutex); + munmap(self->addr, self->size); + close(self->gup_fd); + + if (variant->hugetlb) + hugetlb_restore_settings(); +} + +TEST_F(gup_test, get_user_pages) +{ + /* Tests the get_user_pages path */ + int i; + + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; + + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; + + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, GUP_BASIC_TEST, &gup), 0); + } +} + +TEST_F(gup_test, pin_user_pages) +{ + /* Tests the pin_user_pages path */ + int i; + + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; + + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; + + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, PIN_BASIC_TEST, &gup), 0); } +} =20 -return_: - ksft_test_result(!status, "ioctl status %d\n", status); - return NULL; +TEST_F(gup_test, dump_user_pages_with_get) +{ + /* Tests DUMP_USER_PAGES_TEST using get_user_pages */ + int i; + + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; + + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; + + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + gup.which_pages[0] =3D 1; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0); + } } =20 -int main(int argc, char **argv) +TEST_F(gup_test, dump_user_pages_with_pin) { - struct gup_test gup =3D { 0 }; - int filed, i, opt, nr_pages =3D 1, thp =3D -1, write =3D 1, nthreads =3D = 1, ret; - int flags =3D MAP_PRIVATE; - char *file =3D "/dev/zero"; - bool hugetlb =3D false; - pthread_t *tid; - char *p; + /* Tests DUMP_USER_PAGES_TEST using pin_user_pages */ + int i; =20 - while ((opt =3D getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) !=3D -1= ) { - switch (opt) { - case 'a': - cmd =3D PIN_FAST_BENCHMARK; - break; - case 'b': - cmd =3D PIN_BASIC_TEST; - break; - case 'L': - cmd =3D PIN_LONGTERM_BENCHMARK; - break; - case 'c': - cmd =3D DUMP_USER_PAGES_TEST; - /* - * Dump page 0 (index 1). May be overridden later, by - * user's non-option arguments. - * - * .which_pages is zero-based, so that zero can mean "do - * nothing". - */ - gup.which_pages[0] =3D 1; - break; - case 'p': - /* works only with DUMP_USER_PAGES_TEST */ - gup.test_flags |=3D GUP_TEST_FLAG_DUMP_PAGES_USE_PIN; - break; - case 'F': - /* strtol, so you can pass flags in hex form */ - gup.gup_flags =3D strtol(optarg, 0, 0); - break; - case 'j': - nthreads =3D atoi(optarg); - break; - case 'm': - size =3D atoi(optarg) * MB; - break; - case 'r': - repeats =3D atoi(optarg); - break; - case 'n': - nr_pages =3D atoi(optarg); - if (nr_pages < 0) - nr_pages =3D size / psize(); - break; - case 't': - thp =3D 1; - break; - case 'T': - thp =3D 0; - break; - case 'U': - cmd =3D GUP_BASIC_TEST; - break; - case 'u': - cmd =3D GUP_FAST_BENCHMARK; - break; - case 'w': - write =3D 1; - break; - case 'W': - write =3D 0; - break; - case 'f': - file =3D optarg; - break; - case 'S': - flags &=3D ~MAP_PRIVATE; - flags |=3D MAP_SHARED; - break; - case 'H': - flags |=3D (MAP_HUGETLB | MAP_ANONYMOUS); - hugetlb =3D true; - break; - default: - ksft_exit_fail_msg("Wrong argument\n"); - } + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; + + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; + + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + gup.which_pages[0] =3D 1; + gup.test_flags |=3D GUP_TEST_FLAG_DUMP_PAGES_USE_PIN; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0); } +} =20 - if (optind < argc) { - int extra_arg_count =3D 0; - /* - * For example: - * - * ./gup_test -c 0 1 0x1001 - * - * ...to dump pages 0, 1, and 4097 - */ - - while ((optind < argc) && - (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) { - /* - * Do the 1-based indexing here, so that the user can - * use normal 0-based indexing on the command line. - */ - long page_index =3D strtol(argv[optind], 0, 0) + 1; - - gup.which_pages[extra_arg_count] =3D page_index; - extra_arg_count++; - optind++; - } +TEST_F(gup_test, get_user_pages_fast) +{ + /* Tests the lockless get_user_pages_fast() path */ + int i; + + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; + + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; + + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, GUP_FAST_BENCHMARK, &gup), 0); } +} + +TEST_F(gup_test, pin_user_pages_fast) +{ + /* Tests the lockless pin_user_pages_fast() path */ + int i; =20 - ksft_print_header(); + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; =20 - if (hugetlb) { - unsigned long hp_size =3D default_huge_page_size(); + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; =20 - if (!hp_size) - ksft_exit_skip("HugeTLB is unavailable\n"); + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; =20 - size =3D (size + hp_size - 1) & ~(hp_size - 1); - if (!hugetlb_setup_default(size / hp_size)) - ksft_exit_skip("Not enough huge pages\n"); + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, PIN_FAST_BENCHMARK, &gup), 0); } +} =20 - ksft_set_plan(nthreads); +TEST_F(gup_test, pin_user_pages_longterm) +{ + /* Tests pin_user_pages() with FOLL_LONGTERM */ + int i; =20 - filed =3D open(file, O_RDWR|O_CREAT, 0664); - if (filed < 0) - ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno)); + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; =20 - gup.nr_pages_per_call =3D nr_pages; - if (write) - gup.gup_flags |=3D FOLL_WRITE; + gup.addr =3D (unsigned long)self->addr; + gup.size =3D self->size; + gup.nr_pages_per_call =3D nr_pages_list[i] < 0 ? + self->size / psize() : nr_pages_list[i]; =20 - gup_fd =3D open(GUP_TEST_FILE, O_RDWR); - if (gup_fd =3D=3D -1) { - switch (errno) { - case EACCES: - if (getuid()) - ksft_print_msg("Please run this test as root\n"); - break; - case ENOENT: - if (opendir("/sys/kernel/debug") =3D=3D NULL) - ksft_print_msg("mount debugfs at /sys/kernel/debug\n"); - ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n"= ); - break; - default: - ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno= )); - break; - } - ksft_test_result_skip("Please run this test as root\n"); - ksft_exit_pass(); + if (variant->write) + gup.gup_flags |=3D FOLL_WRITE; + + TH_LOG("nr_pages_per_call=3D%u", gup.nr_pages_per_call); + ASSERT_EQ(ioctl(self->gup_fd, PIN_LONGTERM_BENCHMARK, &gup), 0); } +} + +TEST(dump_user_pages_sparse_indices) +{ + /* Tests sparse multi-index which_pages[] inputs. */ + struct gup_test gup =3D { 0 }; + unsigned long size =3D 128 * MB; + int zero_fd, gup_fd; + char *addr, *p; + + zero_fd =3D open("/dev/zero", O_RDWR); + ASSERT_GE(zero_fd, 0); =20 - p =3D mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0); - if (p =3D=3D MAP_FAILED) - ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); - gup.addr =3D (unsigned long)p; + gup_fd =3D open(GUP_TEST_FILE, O_RDWR); + ASSERT_GE(gup_fd, 0); + + addr =3D mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0= ); + close(zero_fd); + ASSERT_NE(addr, MAP_FAILED); =20 - if (thp =3D=3D 1) - madvise(p, size, MADV_HUGEPAGE); - else if (thp =3D=3D 0) - madvise(p, size, MADV_NOHUGEPAGE); + madvise(addr, size, MADV_HUGEPAGE); =20 - /* Fault them in here, from user space. */ - for (; (unsigned long)p < gup.addr + size; p +=3D psize()) + for (p =3D addr; (unsigned long)p < (unsigned long)addr + size; + p +=3D psize()) p[0] =3D 0; =20 - tid =3D malloc(sizeof(pthread_t) * nthreads); - assert(tid); - for (i =3D 0; i < nthreads; i++) { - ret =3D pthread_create(&tid[i], NULL, gup_thread, &gup); - assert(ret =3D=3D 0); - } - for (i =3D 0; i < nthreads; i++) { - ret =3D pthread_join(tid[i], NULL); - assert(ret =3D=3D 0); + gup.addr =3D (unsigned long)addr; + gup.size =3D size; + gup.nr_pages_per_call =3D size / psize(); + gup.gup_flags =3D FOLL_WRITE; + gup.which_pages[0] =3D 1; + gup.which_pages[1] =3D 20; + gup.which_pages[2] =3D 0x1001; + + /* + * Preserve the old "./gup_test -ct -F 0x1 0 19 0x1000" sparse dump + * coverage after removing command-line parsing from this binary. + */ + ASSERT_EQ(ioctl(gup_fd, DUMP_USER_PAGES_TEST, &gup), 0); + + munmap(addr, size); + close(gup_fd); +} + +int main(int argc, char **argv) +{ + int fd; + char *file =3D "/dev/zero"; + + fd =3D open(file, O_RDWR); + if (fd < 0) { + ksft_print_header(); + ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno)); } + close(fd); =20 - free(tid); + fd =3D open(GUP_TEST_FILE, O_RDWR); + if (fd =3D=3D -1) { + ksft_print_header(); + if (errno =3D=3D EACCES) + ksft_exit_skip("Please run this test as root\n"); + if (errno =3D=3D ENOENT) { + DIR *debugfs =3D opendir("/sys/kernel/debug"); + + if (!debugfs) { + ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n"); + } else { + closedir(debugfs); + ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n"); + } + } + ksft_exit_skip("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno)= ); + } + close(fd); =20 - ksft_exit_pass(); + return test_harness_run(argc, argv); } diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/self= tests/mm/run_vmtests.sh index 8c296dedf047..56df0dcc9640 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -130,30 +130,6 @@ test_selected() { fi } =20 -run_gup_matrix() { - # -t: thp=3Don, -T: thp=3Doff, -H: hugetlb=3Don - local hugetlb_mb=3D256 - - for huge in -t -T "-H -m $hugetlb_mb"; do - # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pi= n-longterm - for test_cmd in -u -U -a -b -L; do - # -w: write=3D1, -W: write=3D0 - for write in -w -W; do - # -S: shared - for share in -S " "; do - # -n: How many pages to fetch together? 512 is special - # because it's default thp size (or 2M on x86), 123 to - # just test partial gup when hit a huge in whatever fo= rm - for num in "-n 1" "-n 512" "-n 123" "-n -1"; do - CATEGORY=3D"gup_test" run_test ./gup_test \ - $huge $test_cmd $write $share $num - done - done - done - done - done -} - # filter 64bit architectures ARCH64STR=3D"arm64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sparc64 x86= _64" if [ -z "$ARCH" ]; then @@ -276,18 +252,7 @@ fi =20 CATEGORY=3D"mmap" run_test ./map_fixed_noreplace =20 -if $RUN_ALL; then - run_gup_matrix -else - # get_user_pages_fast() benchmark - CATEGORY=3D"gup_test" run_test ./gup_test -u -n 1 - CATEGORY=3D"gup_test" run_test ./gup_test -u -n -1 - # pin_user_pages_fast() benchmark - CATEGORY=3D"gup_test" run_test ./gup_test -a -n 1 - CATEGORY=3D"gup_test" run_test ./gup_test -a -n -1 -fi -# Dump pages 0, 19, and 4096, using pin_user_pages: -CATEGORY=3D"gup_test" run_test ./gup_test -ct -F 0x1 0 19 0x1000 +CATEGORY=3D"gup_test" run_test ./gup_test CATEGORY=3D"gup_test" run_test ./gup_longterm =20 CATEGORY=3D"userfaultfd" run_test ./uffd-unit-tests --=20 2.39.5