From nobody Mon May 25 02:54:03 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 78BA44BCAA6; Tue, 19 May 2026 12:05:30 +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=1779192332; cv=none; b=jZ+w/KYa0BHuQTwWXIMTK/Tr5t72J048/87JOmt5eFvlOeNqNTelwhBLH6LsKIf2OMINXfc6A9RY8cHTHc4194Rpl3jCp/MViuOS4sl10EaShL1z/rdoWAAfl77jZ3/UhgHjwzfnHBg6Prycw3OE43BGHLibJKFbqPEdVR1oOZQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779192332; c=relaxed/simple; bh=sHKXDPRz5CC+ddYd15V+FzHYq1MYmq/fvhP1ENudmq8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Tu6FoCha2A/SVst0HKXmNhUMtbhZztTW6WrWPL7G3tw4sKXWtftJpUL8SyG3pu9izJNwoDaQTm2fCSMrEQ3h1TNpqX454otlbBa40YBh8LSbBIMFQZytQQx7tCWPf/nc4ZYrFjglQYepEo8flMVHPIjwGhUknVpGp5buMDUQ7VY= 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=q/Gr1hmu; 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="q/Gr1hmu" 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 BCF2422FA; Tue, 19 May 2026 05:05:24 -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 49D9F3F85F; Tue, 19 May 2026 05:05:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779192329; bh=sHKXDPRz5CC+ddYd15V+FzHYq1MYmq/fvhP1ENudmq8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=q/Gr1hmuelIgB2MsYmOiR8H1U9DYH/D1x0vo5ToA6KtFqdFQ/6NSCBUPXnCcyegdm hlzL71rfzgLS4GItmwZnC4r0x2RcXjImKWJpzlznhFG2TfnFS8hsay0NIgEC5tZK3c msR13Zh0IzizadQ+PrGnM1RIbGBseQB4th/CHCXU= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Jonathan Corbet , Jason Gunthorpe , John Hubbard , Peter Xu , Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Sarthak Sharma Subject: [PATCH v2 1/2] tools/mm: add a standalone GUP microbenchmark Date: Tue, 19 May 2026 17:35:05 +0530 Message-ID: <20260519120506.184512-2-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260519120506.184512-1-sarthak.sharma@arm.com> References: <20260519120506.184512-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. 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 so tools/mm does not depend on kselftest. Add gup_bench to BUILD_TARGETS and INSTALL_TARGETS in tools/mm/Makefile, 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 | 6 +- tools/mm/gup_bench.c | 491 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 497 insertions(+), 3 deletions(-) create mode 100644 tools/mm/gup_bench.c diff --git a/MAINTAINERS b/MAINTAINERS index 98d0a7a1c689..c91165b9280e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16830,6 +16830,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..8e4db797a17a 100644 --- a/tools/mm/Makefile +++ b/tools/mm/Makefile @@ -3,13 +3,13 @@ # 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 =20 -CFLAGS +=3D -Wall -Wextra -I../lib/ -pthread +CFLAGS +=3D -Wall -Wextra -I../lib/ -I../.. -pthread LDFLAGS +=3D $(LIBS) -pthread =20 all: $(BUILD_TARGETS) @@ -23,7 +23,7 @@ $(LIBS): $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) =20 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..2806ee0d7453 --- /dev/null +++ b/tools/mm/gup_bench.c @@ -0,0 +1,491 @@ +// 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 + +#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" + +/* + * Local HugeTLB setup helpers for gup_bench. + * + * These helpers were copied from tools/testing/selftests/mm/ and adjusted= to + * remove the ksft formatting. Keep this copy local so tools/mm does not + * depend on ksft output behavior. + */ + +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 default_huge_page_size(void) +{ + FILE *f =3D fopen("/proc/meminfo", "r"); + unsigned long hpage_size =3D 0; + char buf[256]; + + if (!f) + return 0; + while (fgets(buf, sizeof(buf), f)) { + if (sscanf(buf, "Hugepagesize: %lu kB", &hpage_size) =3D=3D 1) + break; + } + fclose(f); + hpage_size <<=3D 10; + return hpage_size; +} + +static void hugetlb_sysfs_path(char *buf, size_t buflen, + unsigned long size, const char *attr) +{ + snprintf(buf, buflen, "/sys/kernel/mm/hugepages/hugepages-%lukB/%s", + size / 1024, attr); +} + +static unsigned long hugetlb_read_num(const char *path) +{ + char buf[32]; + FILE *f =3D fopen(path, "r"); + unsigned long val =3D 0; + + if (!f) + return 0; + if (fgets(buf, sizeof(buf), f)) + val =3D strtoul(buf, NULL, 10); + fclose(f); + return val; +} + +static void hugetlb_write_num(const char *path, unsigned long num) +{ + FILE *f =3D fopen(path, "w"); + + if (!f) + return; + fprintf(f, "%lu\n", num); + fclose(f); +} + +static unsigned long hugetlb_nr_pages(unsigned long size) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); + return hugetlb_read_num(path); +} + +static void hugetlb_set_nr_pages(unsigned long size, unsigned long nr) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); + hugetlb_write_num(path, nr); +} + +static unsigned long hugetlb_free_pages(unsigned long size) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "free_hugepages"); + return hugetlb_read_num(path); +} + +/* Saved pool size to restore on exit */ +static unsigned long hugetlb_saved_nr; +static unsigned long hugetlb_saved_size; + +static void hugetlb_restore_atexit(void) +{ + if (hugetlb_saved_size) + hugetlb_set_nr_pages(hugetlb_saved_size, hugetlb_saved_nr); +} + +static bool __hugetlb_setup(unsigned long size, unsigned long nr) +{ + unsigned long free =3D hugetlb_free_pages(size); + unsigned long total =3D hugetlb_nr_pages(size); + + if (free >=3D nr) + return true; + + hugetlb_set_nr_pages(size, total + (nr - free)); + + return hugetlb_free_pages(size) >=3D nr; +} + +static bool hugetlb_setup_default(unsigned long nr) +{ + unsigned long hsize =3D default_huge_page_size(); + + if (!hsize) + return false; + + /* Save current pool so we can restore it on exit (only on first call) */ + if (!hugetlb_saved_size) { + hugetlb_saved_size =3D hsize; + hugetlb_saved_nr =3D hugetlb_nr_pages(hsize); + atexit(hugetlb_restore_atexit); + } + + return __hugetlb_setup(hsize, nr); +} + +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"; +} + +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; + + /* 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"); + return 1; + } + 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)); + 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)); + 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); + assert(tid); + 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); + + 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 atoi(optarg) * MB; + break; + case 'n': + run.nr_pages =3D atoi(optarg); + break; + case 'r': + repeats =3D atoi(optarg); + break; + case 'j': + run.nthreads =3D atoi(optarg); + 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) { + if (opendir("/sys/kernel/debug") =3D=3D NULL) + fprintf(stderr, "Mount debugfs at /sys/kernel/debug\n"); + else + 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 May 25 02:54:03 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 211D94C77CA; Tue, 19 May 2026 12:05:36 +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=1779192339; cv=none; b=To1qZhdOlBUN+DxlkLROQ0JYNPbE7khkvDCx1Ez8jxmgFoq2fEZP3tVOuagOOKELpOYwyPTtvEmWxMJ5vSDK+KjERMgXtIUT5UikRlaI/02dNmU1Cjaw68eeYKo51cLokzqTun1LQFqtRNF5XRtlKqYCUsnX0FKJeOxlXNB6zNg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779192339; c=relaxed/simple; bh=yzDH1fe6pPEveZCHQQ7TQ4gFssEnayPY5KiUXGNb6qw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iedtILGV43BXyKTJZo9Rpssa6w3sq5SoLUd6EdjHX//PgcipgAqR0QB4Ox/6yz676XPh0Dv4lWAdR9blX4BwdZzQWjmz0sf9TPOULuu4VB45Vjs2vamA272KsXJuHr/DdmdklR//Rkvg0aAi6mEuvGNDWPjlOaKPQkqrXe3yLFk= 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=VhpmnhQ0; 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="VhpmnhQ0" 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 57A5A22FA; Tue, 19 May 2026 05:05:31 -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 8D6653F85F; Tue, 19 May 2026 05:05:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779192336; bh=yzDH1fe6pPEveZCHQQ7TQ4gFssEnayPY5KiUXGNb6qw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VhpmnhQ0GRfyKMGVvfArpYPMi0SzXF78+iEt3YorseGqcwYxa/sLAW3vFhaw3ZeDq QgEGeZqEul4UCzdPf23qqtwhKMgQYlQdX+93EaTIf5fqDvTFbejSmEm2at0UomFQ8s e7+tPkqa7hWo8GYdgFDoom0rJ82oG3T00h9z7wQE= From: Sarthak Sharma To: Andrew Morton , David Hildenbrand Cc: Jonathan Corbet , Jason Gunthorpe , John Hubbard , Peter Xu , Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Sarthak Sharma Subject: [PATCH v2 2/2] selftests/mm: rewrite gup_test as a standalone harness-based selftest Date: Tue, 19 May 2026 17:35:06 +0530 Message-ID: <20260519120506.184512-3-sarthak.sharma@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260519120506.184512-1-sarthak.sharma@arm.com> References: <20260519120506.184512-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, for 336 total ioctl sweeps and 84 TAP-reported cases. On a Radxa Orion O6 board, ./gup_test completes in 5.07s on average over 10 runs (range: 4.94s - 5.18s). 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 | 536 +++++++++++++--------- tools/testing/selftests/mm/run_vmtests.sh | 37 +- 3 files changed, 325 insertions(+), 260 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 3f841a96f870..d60d48bb9126 100644 --- a/tools/testing/selftests/mm/gup_test.c +++ b/tools/testing/selftests/mm/gup_test.c @@ -9,267 +9,361 @@ #include #include #include -#include -#include #include #include "kselftest.h" #include "vm_util.h" #include "hugepage_settings.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, +}; + +FIXTURE_VARIANT_ADD(gup_test, shared_readonly_thp) +{ + .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, +}; =20 -static char *cmd_to_str(unsigned long cmd) +FIXTURE_VARIANT_ADD(gup_test, shared_readonly_hugetlb) { - 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 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; + + 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) + 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)) + SKIP(return, "Not enough huge pages\n"); + + mmap_flags |=3D (MAP_HUGETLB | MAP_ANONYMOUS); } - return "Unknown command"; + + /* 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); + + 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); + + 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) -{ - 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); +FIXTURE_TEARDOWN(gup_test) { + 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]; =20 -return_: - ksft_test_result(!status, "ioctl status %d\n", status); - return NULL; + 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 -int main(int argc, char **argv) -{ - 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; +TEST_F(gup_test, dump_user_pages_with_get) { + /* Tests DUMP_USER_PAGES_TEST using get_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; + + 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, dump_user_pages_with_pin) { + /* Tests DUMP_USER_PAGES_TEST using pin_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; + 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 - ksft_print_header(); +TEST_F(gup_test, get_user_pages_fast) { + /* Tests the lockless get_user_pages_fast() path */ + int i; =20 - if (hugetlb) { - unsigned long hp_size =3D default_huge_page_size(); + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; =20 - if (!hp_size) - ksft_exit_skip("HugeTLB is unavailable\n"); + 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 - size =3D (size + hp_size - 1) & ~(hp_size - 1); - if (!hugetlb_setup_default(size / hp_size)) - ksft_exit_skip("Not enough huge pages\n"); + 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); } +} =20 - ksft_set_plan(nthreads); +TEST_F(gup_test, pin_user_pages_fast) { + /* Tests the lockless pin_user_pages_fast() path */ + 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_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(); + 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_FAST_BENCHMARK, &gup), 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; +TEST_F(gup_test, pin_user_pages_longterm) { + /* Tests pin_user_pages() with FOLL_LONGTERM */ + int i; =20 - if (thp =3D=3D 1) - madvise(p, size, MADV_HUGEPAGE); - else if (thp =3D=3D 0) - madvise(p, size, MADV_NOHUGEPAGE); + for (i =3D 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) { + struct gup_test gup =3D { 0 }; =20 - /* Fault them in here, from user space. */ - for (; (unsigned long)p < gup.addr + size; p +=3D psize()) - p[0] =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]; =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); + 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); } - for (i =3D 0; i < nthreads; i++) { - ret =3D pthread_join(tid[i], NULL); - assert(ret =3D=3D 0); +} + +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) { + if (opendir("/sys/kernel/debug") =3D=3D NULL) + ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n"); + else + 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 043aa3ed2596..65a4ef0f3748 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 @@ -239,18 +215,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