From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id E5AC0C7EE32 for ; Sat, 28 Jun 2025 11:40:05 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 6F0E76B0098; Sat, 28 Jun 2025 07:40:05 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 6C82D6B00A2; Sat, 28 Jun 2025 07:40:05 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 5DE2E6B00A4; Sat, 28 Jun 2025 07:40:05 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0012.hostedemail.com [216.40.44.12]) by kanga.kvack.org (Postfix) with ESMTP id 426736B0098 for ; Sat, 28 Jun 2025 07:40:05 -0400 (EDT) Received: from smtpin08.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay05.hostedemail.com (Postfix) with ESMTP id 0A530597CC for ; Sat, 28 Jun 2025 11:40:05 +0000 (UTC) X-FDA: 83604615570.08.1D20B78 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) by imf25.hostedemail.com (Postfix) with ESMTP id 152E4A0011 for ; Sat, 28 Jun 2025 11:40:02 +0000 (UTC) Authentication-Results: imf25.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=FnfcXol0; spf=pass (imf25.hostedemail.com: domain of lianux.mm@gmail.com designates 209.85.214.170 as permitted sender) smtp.mailfrom=lianux.mm@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1751110803; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=N6y9Dw+jgZJo4xJlbwrL/3OQAq0W5zElj1YXcefANK4=; b=rtZGN8mbmfwkLL699DXAYnqmoC+0jwbBk9K+AAvB4OK6k8zpPfCw9NtS0yZ/aIBNSQKms2 i1t5lwl7QtqQs/U2HwGf8MU9j9vpNa7ZJM54Ti9SmEu8GTau2nLPikO4PxBd8lBFO0h0R0 vV+F5MteIZ5EjhIy8Eootute28HYfuY= ARC-Authentication-Results: i=1; imf25.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=FnfcXol0; spf=pass (imf25.hostedemail.com: domain of lianux.mm@gmail.com designates 209.85.214.170 as permitted sender) smtp.mailfrom=lianux.mm@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1751110803; a=rsa-sha256; cv=none; b=BwT0cDV5BlJ0CiCP0dmU6RcV36djuZWGg3M8crCx7WtsWi2L+zqgZ+UBYpAYTlmAMTph03 Oge6YDymUvw3mpW5ui/J+7nQHAFK9gmAUaOBnaLpJCWWtUXYjE6RvzEyq2w1IJNUY6lC10 pyXeIEGxK98Hc01TMFLy65glZSI3mEo= Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-236470b2dceso3252985ad.0 for ; Sat, 28 Jun 2025 04:40:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751110802; x=1751715602; darn=kvack.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=N6y9Dw+jgZJo4xJlbwrL/3OQAq0W5zElj1YXcefANK4=; b=FnfcXol04+IGntq/E98r85nAKgwvpTSOi5sdSBOQ1x+hv8I9hW+7RZhxqSJcBMJCHm o+krW77RcoeibJ1fETiwPht9ico9YSj5iknNjVeuQffjISzvuY+RLZKLxoIc8Zqtmw3n vWLibQfhTwM05waz/PUxJPTDiqVmDr3HsJaCCnwovYQynaomZytMJSjVJOW5BzGHYrPk QspO1mTLqG22i01VRy0+KJ5LqQzBvNiORwvoPEN9iSYGd/9Ku4KCHS0UdQ9RlhGEyX/9 rsfaOtEuhhMZE2Im6LZ9sScc+BHli+tqqq52k1Me0HRUuZfGHt9mW9+stFfc7+qlwAE7 5hsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751110802; x=1751715602; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=N6y9Dw+jgZJo4xJlbwrL/3OQAq0W5zElj1YXcefANK4=; b=clGx9BCUcpVhtHuoX+60UjR2gKdqmno077vF/LYE30fMbNCVwjkVo2mAIw6F8VFtIi rHQBeGykVfWhwgbkDpv0zFo9ZLZlVklERtYGUUZ8serDyHhl4Upwj31S+037t/Gfsh9k tBr2MUc9Bikj0n/TBVh3GZsEIQo18nIK+Vj7duaqxeRXcOFgZlMOvmuq8NZn/8jcGOPg 0ERYz+zohvQiLjXLp/nbJLFz0qTd+sjPbN647kz2kIq6ne7kNop8NbRylrQIyY+BZiEl H7sc9WjqDSzlvaqHCfTpT+X1uTxZxILOJQQbE5hMXSjjYCmvQC5JxvHfgXZF1wJpAvXA H9jg== X-Forwarded-Encrypted: i=1; AJvYcCVR4khVHpA5cHjRdXMiH+yddWotupNhOYc6Zc6UlZ3XdnipdSByXn+d73XkdRO0R2NFFT7PMPVyqA==@kvack.org X-Gm-Message-State: AOJu0Yw/xZIgO55/LfWtzQFkJ8LEbfZ/PbCFTROHiW4l5ffs4qm+8Iai CoZ2B+gfSLxZ5R2iaLcvUMF9tDpimVhER2afpKIHWAy+l3F53xAb6JLD X-Gm-Gg: ASbGncuF/Dej3itzj2dLgtmipnLgR/aSxQqjJVNXHUuGQEvpQlQDZHCZpDr0QT4La4y MUl/l7BPjHJSXlYsValmjvhSvbbCz0+2IVBHMuZHkrPLGcJK5NM3Bd5SzZaSV2+aMKRdne+w9XJ SDxNUuqLYYclmGNjS/koVzHDwjk5R5FEXl7EZlispJHAkulbH6np/hfCWjLVhqKx9/834K31bJj 4kH7YHRGPZ8FC8eFZ4co5vHMeDtX0dEOTh7jLxqEbrCCYn7JGVp1VUQpA/OK1gyGQpClpTk5EOU zT5Cu/nfU3esILSPCXiQF9SmU4iUZ9yYE/LSauc8MMzRiXOMbRepfPwvG9OqmQOvGT+I4E/2uqa hXIGcuSD2Ktrk4w5Q X-Google-Smtp-Source: AGHT+IG/DmUME3XbmifZP4XmUZlQ8KnKZYhZROLCSr4UX8yivfQ+99IqRK81G0jjZGl4uUqUWhK8QA== X-Received: by 2002:a17:903:2452:b0:235:f45f:ed41 with SMTP id d9443c01a7336-23ac3afdaadmr107838625ad.19.1751110801639; Sat, 28 Jun 2025 04:40:01 -0700 (PDT) Received: from DESKTOP-GIED850.localdomain ([111.202.167.6]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-23acb2f2239sm38710135ad.81.2025.06.28.04.39.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Jun 2025 04:40:01 -0700 (PDT) From: wang lian To: david@redhat.com, linux-mm@kvack.org, akpm@linux-foundation.org, lorenzo.stoakes@oracle.com Cc: lianux.mm@gmail.com, Liam.Howlett@oracle.com, brauner@kernel.org, gkwang@linx-info.com, jannh@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, p1ucky0923@gmail.com, ryncsn@gmail.com, shuah@kernel.org, sj@kernel.org, vbabka@suse.cz, zijing.zhang@proton.me Subject: [PATCH] selftests/mm: Add process_madvise() tests Date: Sat, 28 Jun 2025 19:39:45 +0800 Message-ID: <20250628113945.145588-1-lianux.mm@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250624104654.4418-1-lianux.mm@gmail.com> References: <20250624104654.4418-1-lianux.mm@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Rspam-User: X-Rspamd-Server: rspam05 X-Rspamd-Queue-Id: 152E4A0011 X-Stat-Signature: xmaa5n9hzk7xzmsxoey4iauq14nkr6ud X-HE-Tag: 1751110802-108511 X-HE-Meta: U2FsdGVkX1/AxVCSmAhtnnoKqH0HpEauFc00zYXh7dp5K5ksZOiUAE5wnmne8y0CWzwsEIrh3xNw0m0mzdFnlIRTnRNZbYfPpXyH99Xedsda8Nu9AsIK2r3LaJaqdLZ+EYTHJ6HCpum0ldhmreO00TFKn29kUgtK2tEXkt7KN+plRbJGG6CMYKlJadHS3Ng7ZzCFi8KARNtuYUX1SAtwUX4/jaLPTaR45CoQLsKxUOgr1pN/wwChW7pqBoQ2+uDj5ybwGUY7dKpivj/IjVKuI/V1Cy5xKilkr/wlfGpxJk0+hS41Mtw/P+N5RDCpK+++GytMS4+iPaKElAW6iZNM6at95He24ESN/g+9nngHQMnwzT5YmZtfAXNUWnc4TZ8NjaywXklZQycclJjneHKWOCbXHCWDi+tRDDXNMpKWAPnQA11fqSpEFCJFAzUm3WXDrC4TyGTu7+GQ8VRlZHI+3b52KYy73RlDQVd1Sb58SItJsukty8WUW1xqkEcqWNaY1Q2GYnIancV1Q8j4wWTr0P5LYj1QBYfLAC4wZtuU0ZF5XDfHLg3neIEssMa2N52/lNu4sedUts0b230zJswWuMtTLTl/3BWaBU5kVKAI/1O1TqbHq1mV5mU+Cq28FQCSKNbFKtKzMXktPlxRrIiN0/m7aIc2VBj/4lRueVmoBGVIBuW5No4Fs8W0s226JvMSD8gPvWF21PwQlFEwDSfPY1h3lDsKS9kLsSXFCaaXhc11Gx8cE14gPJ2KcWSUXP2b4quSKzcX/oAwttkOL3EVEM1rpC1X5KjO/m2xucssk5OYXSQ8wt0+z+MRtdJy1SpiEFvaXypkBAfpFs2i0QOnK5gNEQrEXulBSo8zPXsh3IaA2BdCM/M2GyoNtDwtM4yB8QPJ82lEHpKmfMRvhY+Tofizf9l8dkyQrQjn5Gy1apDCu4sUBEi9rioIDC+mL7SeyBx8jSBHF57D5cWzO31 dEo7gT9Z Y7FZjbnZSp/liXUaSE/VcqJHO9z1iSxjNG6H4nKOqIXQbFGOTxzWuaUgpeGxuBgs6BmVtGStpYmTyxLDYAYyB3Dyj1rHhsY1VIqDzIPzCdXi9G9lXaNJvB0Omdu167MYe3Z+cif5cbs07WSUyXokob5wJObHWBsnCo6iWhBMWXUp56SVPqMrjcaxoTPmux7elegxxOYs4+RB3okaFynyXaqhl3yK8rbh/oDKLWoVUkbSj00X9EcghdaCAZ/9vZYlaMBq9BZClqhTXKK7G26wP3YqwGG39Nb9cCt6UeIH1KBpKraw/VQyz4Rrij7Ir+p4mWy2HTS7sNrvjOKhjvWxYqRnDQG/Zt065hJxOGW3wGRwZ0y5UUaORLvCHQ4Ooub5Gw4JyqF1G4W/lmHLPEnDKnWHUxfjrisQPjqXLSEHLPYNwHa125mHlGMMRl/fW4G2sWvoDEtGirVvW2kkQP3G+j1Gla/wAJZJPFW8fHoLSk+NSxq5Ohupv+fZMNMIwi96oZhuJLgeJIurKqlaPk7u+afVhncEP395XJGlukuRkyuHZ1IyY2BhrnGCE8AXqRuI26r8yxW+W/aaudupfZHckXC/Mi4j8sOOhBHdF4842BfAbFzWR2K0m7IBw7p5bPkzqDgfbeKYmAD4OHbo= X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: This patch adds tests for the process_madvise(), focusing on verifying behavior under various conditions including valid usage and error cases. Changelog v2: - Drop MADV_DONTNEED tests based on feedback - Focus solely on process_madvise() syscall - Improve error handling and structure - Add future-proof flag test - Style and comment cleanups Signed-off-by: wang lian Suggested-by: Lorenzo Stoakes Suggested-by: David Hildenbrand --- tools/testing/selftests/mm/.gitignore | 1 + tools/testing/selftests/mm/Makefile | 1 + tools/testing/selftests/mm/process_madv.c | 414 ++++++++++++++++++++++ tools/testing/selftests/mm/run_vmtests.sh | 5 + 4 files changed, 421 insertions(+) create mode 100644 tools/testing/selftests/mm/process_madv.c diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index 911f39d634be..a8c3be02188c 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -42,6 +42,7 @@ memfd_secret hugetlb_dio pkey_sighandler_tests_32 pkey_sighandler_tests_64 +process_madv soft-dirty split_huge_page_test ksm_tests diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 2352252f3914..725612e09582 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -86,6 +86,7 @@ TEST_GEN_FILES += mseal_test TEST_GEN_FILES += on-fault-limit TEST_GEN_FILES += pagemap_ioctl TEST_GEN_FILES += pfnmap +TEST_GEN_FILES += process_madv TEST_GEN_FILES += thuge-gen TEST_GEN_FILES += transhuge-stress TEST_GEN_FILES += uffd-stress diff --git a/tools/testing/selftests/mm/process_madv.c b/tools/testing/selftests/mm/process_madv.c new file mode 100644 index 000000000000..73999c8e3570 --- /dev/null +++ b/tools/testing/selftests/mm/process_madv.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define _GNU_SOURCE +#include "../kselftest_harness.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vm_util.h" + +#include "../pidfd/pidfd.h" + +/* + * Ignore the checkpatch warning, as per the C99 standard, section 7.14.1.1: + * + * "If the signal occurs other than as the result of calling the abort or raise + * function, the behavior is undefined if the signal handler refers to any + * object with static storage duration other than by assigning a value to an + * object declared as volatile sig_atomic_t" + */ +static volatile sig_atomic_t signal_jump_set; +static sigjmp_buf signal_jmp_buf; + +/* + * Ignore the checkpatch warning, we must read from x but don't want to do + * anything with it in order to trigger a read page fault. We therefore must use + * volatile to stop the compiler from optimising this away. + */ +#define FORCE_READ(x) (*(volatile typeof(x) *)x) + +static void handle_fatal(int c) +{ + if (!signal_jump_set) + return; + + siglongjmp(signal_jmp_buf, c); +} + +FIXTURE(process_madvise) +{ + int pidfd; + int flag; +}; + +static void setup_sighandler(void) +{ + struct sigaction act = { + .sa_handler = &handle_fatal, + .sa_flags = SA_NODEFER, + }; + + sigemptyset(&act.sa_mask); + if (sigaction(SIGSEGV, &act, NULL)) + ksft_exit_fail_perror("sigaction"); +} + +static void teardown_sighandler(void) +{ + struct sigaction act = { + .sa_handler = SIG_DFL, + .sa_flags = SA_NODEFER, + }; + + sigemptyset(&act.sa_mask); + sigaction(SIGSEGV, &act, NULL); +} + +FIXTURE_SETUP(process_madvise) +{ + self->pidfd = PIDFD_SELF; + self->flag = 0; + setup_sighandler(); +}; + +FIXTURE_TEARDOWN_PARENT(process_madvise) +{ + teardown_sighandler(); +} + +static ssize_t sys_process_madvise(int pidfd, const struct iovec *iovec, + size_t vlen, int advice, unsigned int flags) +{ + return syscall(__NR_process_madvise, pidfd, iovec, vlen, advice, flags); +} + +/* + * Enable our signal catcher and try to read/write the specified buffer. The + * return value indicates whether the read/write succeeds without a fatal + * signal. + */ +static bool try_access_buf(char *ptr, bool write) +{ + bool failed; + + /* Tell signal handler to jump back here on fatal signal. */ + signal_jump_set = true; + /* If a fatal signal arose, we will jump back here and failed is set. */ + failed = sigsetjmp(signal_jmp_buf, 0) != 0; + + if (!failed) { + if (write) + *ptr = 'x'; + else + FORCE_READ(ptr); + } + + signal_jump_set = false; + return !failed; +} + +/* Try and read from a buffer, return true if no fatal signal. */ +static bool try_read_buf(char *ptr) +{ + return try_access_buf(ptr, false); +} + +TEST_F(process_madvise, basic) +{ + const unsigned long pagesize = (unsigned long)sysconf(_SC_PAGESIZE); + const int madvise_pages = 4; + char *map; + ssize_t ret; + struct iovec vec[madvise_pages]; + + /* + * Create a single large mapping. We will pick pages from this + * mapping to advise on. This ensures we test non-contiguous iovecs. + */ + map = mmap(NULL, pagesize * 10, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(map, MAP_FAILED); + + /* Fill the entire region with a known pattern. */ + memset(map, 'A', pagesize * 10); + + /* + * Setup the iovec to point to 4 non-contiguous pages + * within the mapping. + */ + vec[0].iov_base = &map[0 * pagesize]; + vec[0].iov_len = pagesize; + vec[1].iov_base = &map[3 * pagesize]; + vec[1].iov_len = pagesize; + vec[2].iov_base = &map[5 * pagesize]; + vec[2].iov_len = pagesize; + vec[3].iov_base = &map[8 * pagesize]; + vec[3].iov_len = pagesize; + + ret = sys_process_madvise(PIDFD_SELF, vec, madvise_pages, MADV_DONTNEED, + 0); + if (ret == -1 && errno == EPERM) + ksft_exit_skip( + "process_madvise() unsupported or permission denied, try running as root.\n"); + else if (errno == EINVAL) + ksft_exit_skip( + "process_madvise() unsupported or parameter invalid, please check arguments.\n"); + + /* The call should succeed and report the total bytes processed. */ + ASSERT_EQ(ret, madvise_pages * pagesize); + + /* Check that advised pages are now zero. */ + for (int i = 0; i < madvise_pages; i++) { + char *advised_page = (char *)vec[i].iov_base; + + /* Access should be successful (kernel provides a new page). */ + ASSERT_TRUE(try_read_buf(advised_page)); + /* Content must be 0, not 'A'. */ + ASSERT_EQ(*advised_page, 0); + } + + /* Check that an un-advised page in between is still 'A'. */ + char *unadvised_page = &map[1 * pagesize]; + + ASSERT_TRUE(try_read_buf(unadvised_page)); + ASSERT_EQ(*unadvised_page, 'A'); + + /* Cleanup. */ + ASSERT_EQ(munmap(map, pagesize * 10), 0); +} + +static long get_smaps_anon_huge_pages(pid_t pid, void *addr) +{ + char smaps_path[64]; + char *line = NULL; + unsigned long start, end; + long anon_huge_kb; + size_t len; + FILE *f; + bool in_vma; + + in_vma = false; + sprintf(smaps_path, "/proc/%d/smaps", pid); + f = fopen(smaps_path, "r"); + if (!f) + return -1; + + while (getline(&line, &len, f) != -1) { + /* Check if the line describes a VMA range */ + if (sscanf(line, "%lx-%lx", &start, &end) == 2) { + if ((unsigned long)addr >= start && + (unsigned long)addr < end) + in_vma = true; + else + in_vma = false; + continue; + } + + /* If we are in the correct VMA, look for the AnonHugePages field */ + if (in_vma && + sscanf(line, "AnonHugePages: %ld kB", &anon_huge_kb) == 1) + break; + } + + free(line); + fclose(f); + + return (anon_huge_kb > 0) ? (anon_huge_kb * 1024) : 0; +} + +/** + * TEST_F(process_madvise, remote_collapse) + * + * This test deterministically validates process_madvise() with MADV_COLLAPSE + * on a remote process, other advices are difficult to verify reliably. + * + * The test verifies that a memory region in a child process, initially + * backed by small pages, can be collapsed into a Transparent Huge Page by a + * request from the parent. The result is verified by parsing the child's + * /proc//smaps file. + */ +TEST_F(process_madvise, remote_collapse) +{ + const unsigned long pagesize = (unsigned long)sysconf(_SC_PAGESIZE); + pid_t child_pid; + int pidfd; + long huge_page_size; + int pipe_info[2]; + ssize_t ret; + struct iovec vec; + + struct child_info { + pid_t pid; + void *map_addr; + } info; + + huge_page_size = default_huge_page_size(); + if (huge_page_size <= 0) + ksft_exit_skip("Could not determine a valid huge page size.\n"); + + ASSERT_EQ(pipe(pipe_info), 0); + + child_pid = fork(); + ASSERT_NE(child_pid, -1); + + if (child_pid == 0) { + char *map; + size_t map_size = 2 * huge_page_size; + + close(pipe_info[0]); + + map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(map, MAP_FAILED); + + /* Fault in as small pages */ + for (size_t i = 0; i < map_size; i += pagesize) + map[i] = 'A'; + + /* Send info and pause */ + info.pid = getpid(); + info.map_addr = map; + ret = write(pipe_info[1], &info, sizeof(info)); + ASSERT_EQ(ret, sizeof(info)); + close(pipe_info[1]); + + pause(); + exit(0); + } + + close(pipe_info[1]); + + /* Receive child info */ + ret = read(pipe_info[0], &info, sizeof(info)); + if (ret <= 0) { + waitpid(child_pid, NULL, 0); + ksft_exit_skip("Failed to read child info from pipe.\n"); + } + ASSERT_EQ(ret, sizeof(info)); + close(pipe_info[0]); + child_pid = info.pid; + + pidfd = pidfd_open(child_pid, 0); + ASSERT_GE(pidfd, 0); + + /* Baseline Check from Parent's perspective */ + ASSERT_EQ(get_smaps_anon_huge_pages(child_pid, info.map_addr), 0); + + vec.iov_base = info.map_addr; + vec.iov_len = huge_page_size; + ret = sys_process_madvise(pidfd, &vec, 1, MADV_COLLAPSE, 0); + if (ret == -1) { + if (errno == EINVAL) + ksft_exit_skip( + "PROCESS_MADV_ADVISE is not supported.\n"); + else if (errno == EPERM) + ksft_exit_skip( + "No process_madvise() permissions, try running as root.\n"); + goto cleanup; + } + ASSERT_EQ(ret, huge_page_size); + + ASSERT_EQ(get_smaps_anon_huge_pages(child_pid, info.map_addr), + huge_page_size); + + ksft_test_result_pass( + "MADV_COLLAPSE successfully verified via smaps.\n"); + +cleanup: + /* Cleanup */ + kill(child_pid, SIGKILL); + waitpid(child_pid, NULL, 0); + if (pidfd >= 0) + close(pidfd); +} + +/* + * Test process_madvise() with various invalid pidfds to ensure correct error + * handling. This includes negative fds, non-pidfd fds, and pidfds for + * processes that no longer exist. + */ +TEST_F(process_madvise, invalid_pidfd) +{ + struct iovec vec; + pid_t child_pid; + ssize_t ret; + int pidfd; + + vec.iov_base = (void *)0x1234; + vec.iov_len = 4096; + + /* Using an invalid fd number (-1) should fail with EBADF. */ + ret = sys_process_madvise(-1, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBADF); + + /* + * Using a valid fd that is not a pidfd (e.g. stdin) should fail + * with EBADF. + */ + ret = sys_process_madvise(STDIN_FILENO, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBADF); + + /* + * Using a pidfd for a process that has already exited should fail + * with ESRCH. + */ + child_pid = fork(); + ASSERT_NE(child_pid, -1); + + if (child_pid == 0) + exit(0); + + pidfd = pidfd_open(child_pid, 0); + ASSERT_GE(pidfd, 0); + + /* Wait for the child to ensure it has terminated. */ + waitpid(child_pid, NULL, 0); + + ret = sys_process_madvise(pidfd, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, ESRCH); + close(pidfd); +} + +/* + * Test process_madvise() with an invalid flag value. Now we only support flag=0 + * future we will use it support sync so reserve this test. + */ +TEST_F(process_madvise, flag) +{ + const unsigned long pagesize = (unsigned long)sysconf(_SC_PAGESIZE); + unsigned int invalid_flag; + struct iovec vec; + char *map; + ssize_t ret; + + map = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + ASSERT_NE(map, MAP_FAILED); + + vec.iov_base = map; + vec.iov_len = pagesize; + + invalid_flag = 0x80000000; + + ret = sys_process_madvise(PIDFD_SELF, &vec, 1, MADV_DONTNEED, + invalid_flag); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); + + /* Cleanup. */ + ASSERT_EQ(munmap(map, pagesize), 0); +} + +TEST_HARNESS_MAIN \ No newline at end of file diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index f96d43153fc0..5c28ebcf1ea9 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -61,6 +61,8 @@ separated by spaces: ksm tests that require >=2 NUMA nodes - pkey memory protection key tests +- process_madvise + test process_madvise - soft_dirty test soft dirty page bit semantics - pagemap @@ -424,6 +426,9 @@ CATEGORY="hmm" run_test bash ./test_hmm.sh smoke # MADV_GUARD_INSTALL and MADV_GUARD_REMOVE tests CATEGORY="madv_guard" run_test ./guard-regions +# PROCESS_MADVISE TEST +CATEGORY="process_madv" run_test ./process_madv + # MADV_DONTNEED and PROCESS_DONTNEED tests CATEGORY="madv_dontneed" run_test ./madv_dontneed -- 2.43.0