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 C04D2C5B543 for ; Wed, 4 Jun 2025 08:22:06 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 616306B04A1; Wed, 4 Jun 2025 04:22:04 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 5EE356B04A5; Wed, 4 Jun 2025 04:22:04 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 503E46B04A6; Wed, 4 Jun 2025 04:22:04 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id 2E59A6B04A1 for ; Wed, 4 Jun 2025 04:22:04 -0400 (EDT) Received: from smtpin08.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay01.hostedemail.com (Postfix) with ESMTP id E041E1D44F7 for ; Wed, 4 Jun 2025 08:22:03 +0000 (UTC) X-FDA: 83517025326.08.E5236F9 Received: from mail-ed1-f50.google.com (mail-ed1-f50.google.com [209.85.208.50]) by imf06.hostedemail.com (Postfix) with ESMTP id 11F58180005 for ; Wed, 4 Jun 2025 08:22:01 +0000 (UTC) Authentication-Results: imf06.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=XqG2mYbG; spf=pass (imf06.hostedemail.com: domain of richard.weiyang@gmail.com designates 209.85.208.50 as permitted sender) smtp.mailfrom=richard.weiyang@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=1749025322; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:in-reply-to: references:references:dkim-signature; bh=QE8vdP8hSc3Jzn2TfzhjI7QNIR3JFcIZuJNht/87/2g=; b=CPOLZOkv7fxRM4GMJwvpmajkje6yMFg1cMj9ffZhMJS23J6sddUhcWJoSRsy4jc973B0IU dPF8IBPLvdW/UtCGqvYN0Jxa1nHLXTZP6cSZOaDNB/FfxRBPNFw4RCeCcA8rfOCb2PRb0G nanBba2DIo9srOF6fjSd13U9mhPAUE8= ARC-Authentication-Results: i=1; imf06.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=XqG2mYbG; spf=pass (imf06.hostedemail.com: domain of richard.weiyang@gmail.com designates 209.85.208.50 as permitted sender) smtp.mailfrom=richard.weiyang@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1749025322; a=rsa-sha256; cv=none; b=537cmTc5aKOgRfO+0cLDWmk/sWv50KgoeZZ1xU3SRvFZE3pQNfvE+nE1gylpjqn0fCohkB v1g6skpeMfWfJQynXGzg0xVDh3kGLSATqdisp4wgaLFM/2SU9Yl/pz2xOZIyGPbvS3ubif M3nKPIfOZJMDnDzEA7a9D0Memg4QN4A= Received: by mail-ed1-f50.google.com with SMTP id 4fb4d7f45d1cf-60700a745e5so423007a12.3 for ; Wed, 04 Jun 2025 01:22:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1749025320; x=1749630120; darn=kvack.org; h=references:in-reply-to:message-id:date:subject:cc:to:from:from:to :cc:subject:date:message-id:reply-to; bh=QE8vdP8hSc3Jzn2TfzhjI7QNIR3JFcIZuJNht/87/2g=; b=XqG2mYbGJPNY8HjLMxo08OCic+NR/3g6r/welKkd0oOWgK06CllBliaADkwN1eO+cy rVdHVvlwWll1M5T1yjacY4I52e2qywwxW8ws7Ai5RvavhCjIixjqc8HX8TvFgr8Tl1nH cHYHm6XRP3COX/xu03B+3229RKT7d7CyM3aWY4sVt7WHhKQJCzXQ5lzTni3YY8gtaT1S DXYfxjxESmhtqF18MNLm1GmW1TlkqGzBYrl9cDIjRPjGW63HkM8+2vxVyLKl2+QmJYPw HhKYdzbC2Eye7Cdsa1on5KDoZp2fZdQs9xe6gDjG91L5voxJSFxVJT33ZBB8jOYtESiL r5Xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749025320; x=1749630120; h=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=QE8vdP8hSc3Jzn2TfzhjI7QNIR3JFcIZuJNht/87/2g=; b=RuclTe2Xf4TlF3l2+uSJmrSnOmG41v9rdhdVaofNLNR+I+Owje3bAC2i8bGVSrDD/A QqT6WkX0n/tFTHV+lLXm7AsBONFFmqoMksewyhSOA8xnTGAoRmRV0T6k37mhwsXxVbh/ pkkIZI2N9EQ1pYlr4eVqcU7O2jXpf3J918LUm9z1yflKnBaiyeIUmVEyjZ0mFv76Q8rp l+fEdCpXLxh1bauXioqfkLJLx/9bJuhoQqrgcJPizhyt1+nXkKlIXkwHUJ7nAaHfaofH dI5r3t1+lhOL83rbbn0q8Jj8Fw0MBQSdtzVxac1QtU15eFrzwWo0lWse5AR9wlNVWkhB JKZg== X-Gm-Message-State: AOJu0YxCuzCMmnGArQjBedxnz649+Rfp4XjYi+eA5ZfQbTd5Xk+RQXwz ifbvlK6Uq2mnYeOmfkJEL+oxEyRVeWxH4qEtOvdushdbs2jbAu6z6nBC X-Gm-Gg: ASbGncuTHFA3RI32dNXEVvQt20TmVLvYGzbfuQPu0qham9Zr1T5E69Mhl0Lv3RmcD6S lYyGEu4ieXewsGlpC/XD+FUfD5iuB7x/EDGvZ3X30oTIi+fVTWKXPn6stMckf3SSo5Rv7ehoJWx JH2umuZUhBDP4pWuwq3DUZIlREyGF9L9Jq5OHiyU0UVrZBsyO1U+hEEPDbnal/R4BnupV184UKJ h6uk1h7zEWZ3PbtPV0Q2RdbSElpAMscLZcc3WhCOmX50X6L4azKGOspfh5r+OUgJp4WAra3KHSv FxcIPiXMz9wnXOaPhKGTBdxl8IF0hqr22MFV6KWv57dya+gcTqg= X-Google-Smtp-Source: AGHT+IGA1vRYfxw2qJBS1JfBJz72Ul9etAVCtzIZ/k5JvEj70ONXbEZhgJVvEAs0TdM1XXfZqQbumw== X-Received: by 2002:a17:907:7fa6:b0:adb:2e5f:faea with SMTP id a640c23a62f3a-addf8f2db75mr172091066b.41.1749025320299; Wed, 04 Jun 2025 01:22:00 -0700 (PDT) Received: from localhost ([185.92.221.13]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ada5d82de9bsm1053049266b.47.2025.06.04.01.21.59 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 04 Jun 2025 01:21:59 -0700 (PDT) From: Wei Yang To: akpm@linux-foundation.org, david@redhat.com, lorenzo.stoakes@oracle.com, riel@surriel.com, Liam.Howlett@oracle.com, vbabka@suse.cz, harry.yoo@oracle.com Cc: linux-mm@kvack.org, linux-kselftest@vger.kernel.org, Wei Yang Subject: [RFC Patch 2/2] selftests/mm: assert rmap behave as expected Date: Wed, 4 Jun 2025 08:21:45 +0000 Message-Id: <20250604082145.13800-3-richard.weiyang@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20250604082145.13800-1-richard.weiyang@gmail.com> References: <20250604082145.13800-1-richard.weiyang@gmail.com> X-Rspam-User: X-Rspamd-Queue-Id: 11F58180005 X-Rspamd-Server: rspam09 X-Stat-Signature: 91eizatnis3hk571izgjbx5qbsumxg93 X-HE-Tag: 1749025321-570971 X-HE-Meta: U2FsdGVkX1+26K5nN+FAw0dvlRsHaa4sKEKxh4ICR9Wt6p2754DhpBmTV1PXfYE5+CXfusXg8PwomLdCDWLoPdOtP95etHwuErNUhwEyHcth4nboa+fHiSa+PP4zDuGfBBtTEowHiRBNVmoFbT9FR65uNquQEwvkmlFZczfAbGPdBp7bMX9GG1gE5Nh1csTJ30BGxH5m+Ur5eBgHVdg0lByFJbgpWpN5O3Ue9Aojmoz6c8vygedGCEu5/PQEY5quMD8Yt+TsHSizyL4mPBrP08740aZq1thRnaI5n2tUPQlEg5WDVc8i8Xtuqf5GxSS8l1kFxuPm4ZzXbX27hVbrCLVIFi3I330n8G+dUgsPnpOc2jMcfGBOhpgZBsW22FmiwbRQqO+OKJ7UTSWM4uQ0HqY2f89rjbWpFN1tteabJ73eHychcH4cba8cm9wwBVGt8T/CuwQCRmtdUSrqLmAOXCVNSVP8lU7EG+hLbZWc30Ef2rhSzu9ZqGz+WzdQG7tLbSLe9ZJAJEW43/i2fzrupB7gUvxacPfKHCJN/017opNOAEXxU9JLW4QpNnB8Mc9vpQlOguHhptBmnW3PqmoC7oYuImQAUxE1CFzE22lxV+5fpie84o7mu44L2USQ0gNijaDhRS7LwGhSR85tjKDYYcvHedBNO7ajuFxUnLQitOAO9sudcv7lYvWAgRTopgCBd4AI/xCXH0MxZe5siWWszUKuH8GwD0jx3duQbN82mmUT9PcCzzt8x9fU5NxAhuHiaCvJ2jd6pjroQ+p6XLnb9rIY1JjDipEC2dDIn47IkEhnRRV0r3Z7WctTVsdB1JJMlLe7Ipb/c29X6dZ8RnM+dej0nvcGP2eairwjlPmSypCyh5YmlKjNAOegULWzGD1Vfge++3qqm8kmSgpnulx9YbvFCyrVaqsRY1XvIwK+mOOjPyy/VJfn41o2Z292ZMj5vi/EPrVeGmHkEzQfvCm jbV6jOW8 lmwqGK0AEyIiFavuIGu7MOzsZTM6edFhe22F+NBrWfRSrnhODRo78BI+R4rVWwWqxCTRbtIUcBJNDozVhWG5qoOFgRss0HGqT7PzYLdxkDTeJfsrZOtastQWVv7L1YrxRk/j5gUMkpU5Ccs4v1zqZwoBdJPv2X+Byyg+T/fdTxgBlEvkfTqOkyA5w2QXUzRvzsUlS+UrHPF/MqMPsIi313ALgWphAD1cIbWOHN3meqY3dU3YXsKPu5bfJZLrECo54eEhTEL100n8Dg/cTeHDSYkbO3V0SVsxoRzNPGCC3Qxt/kj6WspT+vSWbC3vdQ28I0Hn7c81j4NfcsS5QbG/uD6Lm1Q8DDKIRsCCC/f9QjxzZyjHeTgurQ3pZKZz789oyJ7K1ferGQmqOcdegP5vQ53O//AGgnwbWuVUEyMZam4lAviD2LEaTLpZ/j1I5ZlsYvEPLxoAqREmGnPEhMEjAg+RyV0rwPBFG+388KBI533OmH1Gyw0qZ6vYxfNYgjs0XsItjEeTTfbwmiSBqyNe4KUwH78b+FXBK7mbYQv18tsfFM5EJFO3hEvwAbKeFLkCfUjZ3pHlUbTwijITrq9Uv/f5oJbHHE8rBNc9gzZJciyMjuMw36IWxS0x3zlztunkXhA0nkBPtlTcRelQEPf8Qn9qWMQUPKls5nW2g/2s6u1ObQozI9ZFs4dd1vLG30JbVfOnhpE2QBXb4EOE= 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: As David suggested, currently we don't have a high level test case to verify the behavior of rmap. This patch introduce the verification on rmap by migration. The general idea is if migrate one shared page between processes, this would be reflected in all related processes. Otherwise, we have problem in rmap. Currently it covers following four scenarios: * anonymous page * shmem page * pagecache page * ksm page Signed-off-by: Wei Yang Suggested-by: David Hildenbrand Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Rik van Riel Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Harry Yoo --- MAINTAINERS | 1 + tools/testing/selftests/mm/.gitignore | 1 + tools/testing/selftests/mm/Makefile | 3 + tools/testing/selftests/mm/rmap.c | 466 ++++++++++++++++++++++ tools/testing/selftests/mm/run_vmtests.sh | 4 + 5 files changed, 475 insertions(+) create mode 100644 tools/testing/selftests/mm/rmap.c diff --git a/MAINTAINERS b/MAINTAINERS index 0ecc6063b2b5..879138db85e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15664,6 +15664,7 @@ L: linux-mm@kvack.org S: Maintained F: include/linux/rmap.h F: mm/rmap.c +F: tools/testing/selftests/mm/rmap.c MEMORY MANAGEMENT - SECRETMEM M: Andrew Morton diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index 824266982aa3..ff4428d8bc5d 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -60,3 +60,4 @@ pkey_sighandler_tests_32 pkey_sighandler_tests_64 guard-regions merge +rmap diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index ae6f994d3add..c7e3a19b5555 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -100,6 +100,7 @@ TEST_GEN_FILES += hugetlb_dio TEST_GEN_FILES += droppable TEST_GEN_FILES += guard-regions TEST_GEN_FILES += merge +TEST_GEN_FILES += rmap ifneq ($(ARCH),arm64) TEST_GEN_FILES += soft-dirty @@ -227,6 +228,8 @@ $(OUTPUT)/ksm_tests: LDLIBS += -lnuma $(OUTPUT)/migration: LDLIBS += -lnuma +$(OUTPUT)/rmap: LDLIBS += -lnuma + local_config.mk local_config.h: check_config.sh /bin/sh ./check_config.sh $(CC) diff --git a/tools/testing/selftests/mm/rmap.c b/tools/testing/selftests/mm/rmap.c new file mode 100644 index 000000000000..5b3ea5252d83 --- /dev/null +++ b/tools/testing/selftests/mm/rmap.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RMAP functional tests + * + * Author(s): Wei Yang + */ + +#include "../kselftest_harness.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vm_util.h" + +#define TOTAL_LEVEL 5 +#define MAX_CHILDREN 3 + +#define FAIL_ON_CHECK (1 << 0) +#define FAIL_ON_WORK (1 << 1) + +struct sembuf sem_wait = {0, -1, 0}; +struct sembuf sem_signal = {0, 1, 0}; + +static const char initial_data[] = "Hello, world 0!"; +static const char updated_data[] = "Hello, World 0!"; + +enum backend_type { + ANON, + SHM, + NORM_FILE, +}; + +#define PREFIX "kst_rmap" +#define MAX_FILENAME_LEN 256 +const char *suffixes[] = { + "", + "_shm", + "_norm", +}; + +struct global_data; +typedef int (*work_fn)(struct global_data *data); +typedef int (*check_fn)(struct global_data *data); +typedef void (*prepare_fn)(struct global_data *data); + +struct global_data { + int worker_level; + + int semid; + int pipefd[2]; + + unsigned int mapsize; + unsigned int rand_seed; + char *region; + + prepare_fn do_prepare; + work_fn do_work; + check_fn do_check; + + enum backend_type backend; + char filename[MAX_FILENAME_LEN]; + + unsigned long *expected_pfn; +}; + +/* + * Create a process tree with TOTAL_LEVEL height and at most MAX_CHILDREN + * children for each. + * + * It will randomly select one process as 'worker' process which will + * 'do_work' until all processes are created. And all other processes will + * wait until 'worker' finish its work. + */ +int propagate_children(struct global_data *data) +{ + pid_t pid; + unsigned int num_child; + int status; + int ret = 0; + int curr_child, worker_child; + int curr_level = 1; + bool is_worker = true; + +repeat: + num_child = rand_r(&data->rand_seed) % MAX_CHILDREN + 1; + worker_child = is_worker ? rand_r(&data->rand_seed) % num_child : -1; + + for (curr_child = 0; curr_child < num_child; curr_child++) { + pid = fork(); + + if (pid < 0) { + perror("Error: fork\n"); + } else if (pid == 0) { + curr_level++; + + if (curr_child != worker_child) + is_worker = false; + + if (curr_level == TOTAL_LEVEL) + break; + + data->rand_seed += curr_child; + goto repeat; + } + } + + if (data->do_prepare) + data->do_prepare(data); + + close(data->pipefd[1]); + + if (is_worker && curr_level == data->worker_level) { + /* This is the worker process, first wait last process created */ + char buf; + + while (read(data->pipefd[0], &buf, 1) > 0) + ; + + if (data->do_work) + ret = data->do_work(data); + + /* Kick others */ + semctl(data->semid, 0, IPC_RMID); + } else { + /* Wait worker finish */ + semop(data->semid, &sem_wait, 1); + if (data->do_check) + ret = data->do_check(data); + } + + /* Wait all child to quit */ + while (wait(&status) > 0) { + if (WIFEXITED(status)) + ret |= WEXITSTATUS(status); + } + + return ret; +} + +FIXTURE(migrate) +{ + struct global_data data; +}; + +FIXTURE_SETUP(migrate) +{ + struct global_data *data = &self->data; + + ASSERT_EQ(numa_available(), 0); + if (numa_bitmask_weight(numa_all_nodes_ptr) <= 1) + SKIP(return, "Not enough NUMA nodes available"); + + data->mapsize = getpagesize(); + + /* Prepare semaphore */ + data->semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); + ASSERT_NE(data->semid, -1); + ASSERT_NE(semctl(data->semid, 0, SETVAL, 0), -1); + + /* Prepare pipe */ + ASSERT_NE(pipe(data->pipefd), -1); + + data->rand_seed = time(NULL); + srand(data->rand_seed); + + data->worker_level = rand() % TOTAL_LEVEL + 1; + + data->do_prepare = NULL; + data->do_work = NULL; + data->do_check = NULL; + + data->backend = ANON; +}; + +FIXTURE_TEARDOWN(migrate) +{ + struct global_data *data = &self->data; + + if (data->region != MAP_FAILED) + munmap(data->region, data->mapsize); + data->region = MAP_FAILED; + if (data->expected_pfn != MAP_FAILED) + munmap(data->expected_pfn, sizeof(unsigned long)); + data->expected_pfn = MAP_FAILED; + semctl(data->semid, 0, IPC_RMID); + data->semid = -1; + + close(data->pipefd[0]); + + data->do_work = NULL; + data->do_check = NULL; + + switch (data->backend) { + case ANON: + break; + case SHM: + shm_unlink(data->filename); + break; + case NORM_FILE: + unlink(data->filename); + break; + } +} + +int try_to_move_page(char *region) +{ + int ret; + int node; + int status = 0; + + ksft_print_msg("worker %d move_pages of content: %.15s\n", getpid(), region); + + ret = move_pages(0, 1, (void **)®ion, NULL, &status, MPOL_MF_MOVE_ALL); + if (ret != 0) + return FAIL_ON_WORK; + + /* Pick up a different target node */ + for (node = 0; node <= numa_max_node(); node++) { + if (numa_bitmask_isbitset(numa_all_nodes_ptr, node) && node != status) + break; + } + + if (node > numa_max_node()) { + ksft_print_msg("Couldn't find available numa node for testing\n"); + return FAIL_ON_WORK; + } + + ret = move_pages(0, 1, (void **)®ion, &node, &status, MPOL_MF_MOVE_ALL); + if (ret != 0) + return FAIL_ON_WORK; + + return 0; +} + +int move_and_update(struct global_data *data) +{ + int ret; + + ret = try_to_move_page(data->region); + if (ret != 0) + return ret; + + /* Change the content */ + strcpy(data->region, updated_data); + + return ret; +} + +int data_updated(struct global_data *data) +{ + if (data->region == MAP_FAILED) + return 0; + + if (strncmp((char *)data->region, updated_data, strlen(updated_data))) + return FAIL_ON_CHECK; + return 0; +} + +TEST_F(migrate, anon) +{ + pid_t root_pid; + int ret; + struct global_data *data = &self->data; + + /* Map a shared area and fault in */ + data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(data->region, MAP_FAILED); + strcpy(data->region, initial_data); + + data->do_work = move_and_update; + data->do_check = data_updated; + + root_pid = getpid(); + + ret = propagate_children(data); + + if (getpid() == root_pid) { + if (ret & FAIL_ON_WORK) + SKIP(return, "Failed on moving page"); + + ASSERT_EQ(ret, 0); + } else { + exit(ret); + } +} + +TEST_F(migrate, shm) +{ + pid_t root_pid; + int ret; + int shm_fd; + struct global_data *data = &self->data; + + snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[SHM]); + shm_fd = shm_open(data->filename, O_CREAT | O_RDWR, 0666); + ASSERT_NE(shm_fd, -1); + ftruncate(shm_fd, data->mapsize); + data->backend = SHM; + + /* Map a shared area and fault in */ + data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED, shm_fd, 0); + ASSERT_NE(data->region, MAP_FAILED); + strcpy(data->region, initial_data); + close(shm_fd); + + data->do_work = move_and_update; + data->do_check = data_updated; + + root_pid = getpid(); + + ret = propagate_children(data); + + if (getpid() == root_pid) { + if (ret & FAIL_ON_WORK) + SKIP(return, "Failed on moving page"); + + ASSERT_EQ(ret, 0); + } else { + exit(ret); + } +} + +TEST_F(migrate, file) +{ + pid_t root_pid; + int ret; + int fd; + struct global_data *data = &self->data; + + snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[NORM_FILE]); + fd = open(data->filename, O_CREAT | O_RDWR | O_EXCL, 0666); + ASSERT_NE(fd, -1); + ftruncate(fd, data->mapsize); + data->backend = NORM_FILE; + + /* Map a shared area and fault in */ + data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ASSERT_NE(data->region, MAP_FAILED); + strcpy(data->region, initial_data); + close(fd); + + data->do_work = move_and_update; + data->do_check = data_updated; + + root_pid = getpid(); + + ret = propagate_children(data); + + if (getpid() == root_pid) { + if (ret & FAIL_ON_WORK) + SKIP(return, "Failed on moving page"); + + ASSERT_EQ(ret, 0); + } else { + exit(ret); + } +} + +void prepare_local_region(struct global_data *data) +{ + /* Allocate range and set the same data */ + data->region = mmap(NULL, data->mapsize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0); + if (data->region == MAP_FAILED) + return; + + memset(data->region, 0xcf, data->mapsize); +} + +int merge_and_migrate(struct global_data *data) +{ + long start_scans, end_scans; + int ksm_fd, ksm_full_scans_fd, pagemap_fd; + int ret = 0; + + if (data->region == MAP_FAILED) + return FAIL_ON_WORK; + + ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR); + ksm_full_scans_fd = open("/sys/kernel/mm/ksm/full_scans", O_RDONLY); + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + + if (ksm_fd == -1 || ksm_full_scans_fd == -1 || pagemap_fd == -1) + return FAIL_ON_WORK; + + /* Wait for two full scans such that any possible merging happened. */ + start_scans = ksm_get_full_scans(ksm_full_scans_fd); + if (start_scans < 0) + return FAIL_ON_WORK; + if (ksm_start_and_merge(ksm_fd) != 1) + return FAIL_ON_WORK; + do { + end_scans = ksm_get_full_scans(ksm_full_scans_fd); + if (end_scans < 0) + return FAIL_ON_WORK; + } while (end_scans < start_scans + 3); + + ret = try_to_move_page(data->region); + + *data->expected_pfn = pagemap_get_pfn(pagemap_fd, data->region); + + return ret; +} + +int has_same_pfn(struct global_data *data) +{ + unsigned long pfn; + int pagemap_fd; + + if (data->region == MAP_FAILED) + return 0; + + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + if (pagemap_fd == -1) + return FAIL_ON_CHECK; + + pfn = pagemap_get_pfn(pagemap_fd, data->region); + if (pfn != *data->expected_pfn) + return FAIL_ON_CHECK; + + return 0; +} + +TEST_F(migrate, ksm) +{ + pid_t root_pid; + int ret; + struct global_data *data = &self->data; + + ASSERT_EQ(prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0), 0); + data->do_prepare = prepare_local_region; + data->do_work = merge_and_migrate; + data->do_check = has_same_pfn; + + data->expected_pfn = mmap(0, sizeof(unsigned long), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(data->expected_pfn, MAP_FAILED); + + root_pid = getpid(); + + ret = propagate_children(data); + + if (getpid() == root_pid) { + if (ret & FAIL_ON_WORK) + SKIP(return, "Failed in worker"); + + ASSERT_EQ(ret, 0); + } else { + exit(ret); + } +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index dddd1dd8af14..652386999756 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -83,6 +83,8 @@ separated by spaces: test handling of page fragment allocation and freeing - vma_merge test VMA merge cases behave as expected +- rmap + test rmap behave as expected example: ./run_vmtests.sh -t "hmm mmap ksm" EOF @@ -521,6 +523,8 @@ CATEGORY="page_frag" run_test ./test_page_frag.sh aligned CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned +CATEGORY="rmap" run_test ./rmap + echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix echo "1..${count_total}" | tap_output -- 2.34.1