From nobody Thu Apr 9 07:52:54 2026 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20287C43219 for ; Thu, 3 Nov 2022 10:08:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231522AbiKCKI2 (ORCPT ); Thu, 3 Nov 2022 06:08:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44012 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231381AbiKCKIZ (ORCPT ); Thu, 3 Nov 2022 06:08:25 -0400 Received: from madras.collabora.co.uk (madras.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e5ab]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0E31DFA3; Thu, 3 Nov 2022 03:08:09 -0700 (PDT) Received: from localhost.localdomain (unknown [39.45.244.84]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: usama.anjum) by madras.collabora.co.uk (Postfix) with ESMTPSA id 774F3660294A; Thu, 3 Nov 2022 10:08:02 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1667470088; bh=626n+z9DNf3YKf+IKKCXggA9tiB1CJ99myronEy1b5w=; h=From:To:Subject:Date:In-Reply-To:References:From; b=IAe6uC2UurFHIMqmG2tZEY06ursiiiOZowEtBLfuh5WjoddoUjyrfEBEs2qDJZs9v 08zA7+H9E+WbyCUKXiCXPg1oJgzpG5Scn/2o2nn4l5OlwoSZqdL1aKaOHGGUr4uVK2 pda7/LuU5cyXi+YRuVM5wg5C7eJJZ1X0AkveOcfxvKBnSjGl2/lHuS2JmrVp+H+RNb wdf84agZsRyqxkIMM7flSdSuSQF4Q1MWkmfejRsRcJ/kxAwbMWcTS9MNyfzx1WTlBY 0lxPPsaJIpl/Q4RnFlRVtAKj3MkCI/CjBTpu9PGcLgFwOi+0UAab+w3FDyHkThoIOk yG3h0g/1Uwa1A== From: Muhammad Usama Anjum To: Andrei Vagin , Danylo Mocherniuk , Alexander Viro , Andrew Morton , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Suren Baghdasaryan , Greg KH , Christian Brauner , Peter Xu , Yang Shi , Vlastimil Babka , "Zach O'Keefe" , "Matthew Wilcox (Oracle)" , "Gustavo A. R. Silva" , Dan Williams , Muhammad Usama Anjum , kernel@collabora.com, Gabriel Krisman Bertazi , David Hildenbrand , Peter Enderborg , "open list : KERNEL SELFTEST FRAMEWORK" , Shuah Khan , open list , "open list : PROC FILESYSTEM" , "open list : MEMORY MANAGEMENT" Subject: [PATCH v4 3/3] selftests: vm: add pagemap ioctl tests Date: Thu, 3 Nov 2022 15:07:36 +0500 Message-Id: <20221103100736.2356351-4-usama.anjum@collabora.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221103100736.2356351-1-usama.anjum@collabora.com> References: <20221103100736.2356351-1-usama.anjum@collabora.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add pagemap ioctl tests. Add several different types of tests to judge the correction of the interface. Signed-off-by: Muhammad Usama Anjum --- Changes in v4: - Updated all the tests to conform to new IOCTL Changes in v3: - Add another test to do sanity of flags Changes in v2: - Update the tests to use the ioctl interface instead of syscall TAP version 13 1..59 ok 1 sanity_tests_sd wrong flag specified ok 2 sanity_tests_sd wrong mask specified ok 3 sanity_tests_sd wrong return mask specified ok 4 sanity_tests_sd mixture of correct and wrong flag ok 5 sanity_tests_sd Clear area with larger vec size ok 6 sanity_tests_sd Repeated pattern of dirty and non-dirty pages ok 7 sanity_tests_sd Repeated pattern of dirty and non-dirty pages in parts ok 8 sanity_tests_sd Two regions ok 9 Page testing: all new pages must be soft dirty ok 10 Page testing: all pages must not be soft dirty ok 11 Page testing: all pages dirty other than first and the last one ok 12 Page testing: only middle page dirty ok 13 Page testing: only two middle pages dirty ok 14 Page testing: only get 2 dirty pages and clear them as well ok 15 Page testing: Range clear only ok 16 Large Page testing: all new pages must be soft dirty ok 17 Large Page testing: all pages must not be soft dirty ok 18 Large Page testing: all pages dirty other than first and the last one ok 19 Large Page testing: only middle page dirty ok 20 Large Page testing: only two middle pages dirty ok 21 Large Page testing: only get 2 dirty pages and clear them as well ok 22 Large Page testing: Range clear only ok 23 Huge page testing: all new pages must be soft dirty ok 24 Huge page testing: all pages must not be soft dirty ok 25 Huge page testing: all pages dirty other than first and the last one ok 26 Huge page testing: only middle page dirty ok 27 Huge page testing: only two middle pages dirty ok 28 Huge page testing: only get 2 dirty pages and clear them as well ok 29 Huge page testing: Range clear only ok 30 Performance Page testing: all new pages must be soft dirty ok 31 Performance Page testing: all pages must not be soft dirty ok 32 Performance Page testing: all pages dirty other than first and the la= st one ok 33 Performance Page testing: only middle page dirty ok 34 Performance Page testing: only two middle pages dirty ok 35 Performance Page testing: only get 2 dirty pages and clear them as we= ll ok 36 Performance Page testing: Range clear only ok 37 hpage_unit_tests all new huge page must be dirty ok 38 hpage_unit_tests all the huge page must not be dirty ok 39 hpage_unit_tests all the huge page must be dirty and clear ok 40 hpage_unit_tests only middle page dirty ok 41 hpage_unit_tests clear first half of huge page ok 42 hpage_unit_tests clear first half of huge page with limited buffer ok 43 hpage_unit_tests clear second half huge page ok 44 unmapped_region_tests Get dirty pages ok 45 unmapped_region_tests Get dirty pages ok 46 Test test_simple ok 47 sanity_tests clear op can only be specified with PAGE_IS_DIRTY ok 48 sanity_tests rmask specified ok 49 sanity_tests amask specified ok 50 sanity_tests emask specified ok 51 sanity_tests rmask and amask specified ok 52 sanity_tests rmask and amask specified ok 53 sanity_tests Get sd and present pages with amask ok 54 sanity_tests Get all the pages with rmask ok 55 sanity_tests Get sd and present pages with rmask and amask ok 56 sanity_tests Don't get sd pages ok 57 sanity_tests Don't get present pages ok 58 sanity_tests Find dirty present pages with return mask ok 59 sanity_tests Memory mapped file # Totals: pass:59 fail:0 xfail:0 xpass:0 skip:0 error:0 --- tools/testing/selftests/vm/.gitignore | 1 + tools/testing/selftests/vm/Makefile | 5 +- tools/testing/selftests/vm/pagemap_ioctl.c | 681 +++++++++++++++++++++ 3 files changed, 685 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/vm/pagemap_ioctl.c diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftest= s/vm/.gitignore index 8a536c731e3c..4a73983e3e58 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -17,6 +17,7 @@ mremap_dontunmap mremap_test on-fault-limit transhuge-stress +pagemap_ioctl protection_keys protection_keys_32 protection_keys_64 diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/= vm/Makefile index 0986bd60c19f..2325bcdb9fae 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -24,9 +24,8 @@ MACHINE ?=3D $(shell echo $(uname_M) | sed -e 's/aarch64.= */arm64/' -e 's/ppc64.*/p # things despite using incorrect values such as an *occasionally* incomple= te # LDLIBS. MAKEFLAGS +=3D --no-builtin-rules - CFLAGS =3D -Wall -I $(top_srcdir) -I $(top_srcdir)/usr/include $(EXTRA_CFL= AGS) $(KHDR_INCLUDES) -LDLIBS =3D -lrt -lpthread +LDLIBS =3D -lrt -lpthread -lm TEST_GEN_FILES =3D anon_cow TEST_GEN_FILES +=3D compaction_test TEST_GEN_FILES +=3D gup_test @@ -52,6 +51,7 @@ TEST_GEN_FILES +=3D on-fault-limit TEST_GEN_FILES +=3D thuge-gen TEST_GEN_FILES +=3D transhuge-stress TEST_GEN_FILES +=3D userfaultfd +TEST_GEN_PROGS +=3D pagemap_ioctl TEST_GEN_PROGS +=3D soft-dirty TEST_GEN_PROGS +=3D split_huge_page_test TEST_GEN_FILES +=3D ksm_tests @@ -103,6 +103,7 @@ $(OUTPUT)/anon_cow: vm_util.c $(OUTPUT)/khugepaged: vm_util.c $(OUTPUT)/ksm_functional_tests: vm_util.c $(OUTPUT)/madv_populate: vm_util.c +$(OUTPUT)/pagemap_ioctl: vm_util.c $(OUTPUT)/soft-dirty: vm_util.c $(OUTPUT)/split_huge_page_test: vm_util.c $(OUTPUT)/userfaultfd: vm_util.c diff --git a/tools/testing/selftests/vm/pagemap_ioctl.c b/tools/testing/sel= ftests/vm/pagemap_ioctl.c new file mode 100644 index 000000000000..c55a0efa39f5 --- /dev/null +++ b/tools/testing/selftests/vm/pagemap_ioctl.c @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include "vm_util.h" +#include "../kselftest.h" +#include +#include +#include +#include +#include + +#define PAGEMAP_OP_MASK (PAGE_IS_SD | PAGE_IS_FILE | \ + PAGE_IS_PRESENT | PAGE_IS_SWAPED) +#define TEST_ITERATIONS 10 +#define PAGEMAP "/proc/self/pagemap" +int pagemap_fd; + +static long pagemap_ioctl(void *start, int len, void *vec, int vec_len, in= t flag, + int max_pages, int rmask, int amask, int emask, int return_mask) +{ + struct pagemap_scan_arg arg; + int ret; + + arg.start =3D (uintptr_t)start; + arg.len =3D len; + arg.vec =3D (uintptr_t)vec; + arg.vec_len =3D vec_len; + arg.flags =3D flag; + arg.max_pages =3D max_pages; + arg.rmask =3D rmask; + arg.amask =3D amask; + arg.emask =3D emask; + arg.return_mask =3D return_mask; + + ret =3D ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); + + return ret; +} + +int sanity_tests_sd(int page_size) +{ + char *mem, *m[2]; + int mem_size, vec_size, ret, ret2, i; + struct page_region *vec; + + /* 1. wrong operation */ + vec_size =3D 100; + mem_size =3D 10 * page_size; + + vec =3D malloc(sizeof(struct page_region) * vec_size); + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem || !vec) + ksft_exit_fail_msg("error nomem\n"); + + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, -1, + 0, PAGE_IS_SD, 0, 0, PAGE_IS_SD) < 0, + "%s wrong flag specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 8, + 0, 0x1111, 0, 0, PAGE_IS_SD) < 0, + "%s wrong mask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, + 0, PAGE_IS_SD, 0, 0, 0x1000) < 0, + "%s wrong return mask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, PAGEMAP_SD_C= LEAR | 0x32, + 0, PAGE_IS_SD, 0, 0, PAGE_IS_SD) < 0, + "%s mixture of correct and wrong flag\n", __func__); + + /* 2. Clear area with larger vec size */ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + ksft_test_result(ret >=3D 0, "%s Clear area with larger vec size\n", __fu= nc__); + + /* 3. Repeated pattern of dirty and non-dirty pages */ + for (i =3D 0; i < mem_size; i +=3D 2 * page_size) + mem[i]++; + + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, PAGE_IS_SD, 0, = 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D mem_size/(page_size * 2), + "%s Repeated pattern of dirty and non-dirty pages\n", __func__); + + /* 4. Repeated pattern of dirty and non-dirty pages in parts*/ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, mem_size/(page_size *= 2) - 2, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ret2 =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, PAGE_IS_SD, 0,= 0, PAGE_IS_SD); + if (ret2 < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret2, errno, strerror(errno)); + + ksft_test_result((ret + ret2) =3D=3D mem_size/(page_size * 2), + "%s Repeated pattern of dirty and non-dirty pages in parts\n", __func_= _); + + munmap(mem, mem_size); + + /* 5. Two regions */ + m[0] =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_A= NON, -1, 0); + if (!m[0]) + ksft_exit_fail_msg("error nomem\n"); + m[1] =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_A= NON, -1, 0); + if (!m[1]) + ksft_exit_fail_msg("error nomem\n"); + + ret =3D pagemap_ioctl(m[0], mem_size, NULL, 0, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ret =3D pagemap_ioctl(m[1], mem_size, vec, 1, 0, 0, PAGE_IS_SD, 0, 0, PAG= E_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len =3D=3D mem_size/page_size, + "%s Two regions\n", __func__); + + munmap(m[0], mem_size); + munmap(m[1], mem_size); + + free(vec); + return 0; +} + +int base_tests(char *prefix, char *mem, int mem_size, int page_size, int s= kip, int flags) +{ + int vec_size, ret, dirty, dirty2; + struct page_region *vec, *vec2; + + if (skip) { + ksft_test_result_skip("%s all new pages must be soft dirty\n", prefix); + ksft_test_result_skip("%s all pages must not be soft dirty\n", prefix); + ksft_test_result_skip("%s all pages dirty other than first and the last = one\n", + prefix); + ksft_test_result_skip("%s only middle page dirty\n", prefix); + ksft_test_result_skip("%s only two middle pages dirty\n", prefix); + ksft_test_result_skip("%s only get 2 dirty pages and clear them as well\= n", prefix); + ksft_test_result_skip("%s Range clear only\n", prefix); + return 0; + } + + vec_size =3D mem_size/page_size; + vec =3D malloc(sizeof(struct page_region) * vec_size); + vec2 =3D malloc(sizeof(struct page_region) * vec_size); + + /* 1. all new pages must be soft dirty if PAGEMAP_NO_REUSED_REGIONS isn't= used */ + dirty =3D pagemap_ioctl(mem, mem_size, vec, 1, flags | PAGEMAP_SD_CLEAR, = vec_size - 2, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + dirty2 =3D pagemap_ioctl(mem, mem_size, vec2, 1, flags | PAGEMAP_SD_CLEAR= , 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty2 < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty2, errno, strerror(errno)); + + if (flags !=3D PAGEMAP_NO_REUSED_REGIONS) + ksft_test_result(dirty =3D=3D 1 && vec[0].start =3D=3D (unsigned long)me= m && + vec[0].len =3D=3D vec_size - 2 && vec[0].bitmap =3D=3D PAGE_IS_SD && + dirty2 =3D=3D 1 && + vec2[0].start =3D=3D (unsigned long)(mem + mem_size - (2 * page_size)) + && vec2[0].len =3D=3D 2 && vec[0].bitmap =3D=3D PAGE_IS_SD, + "%s all new pages must be soft dirty\n", prefix); + else + ksft_test_result(dirty =3D=3D 0 && dirty2 =3D=3D 0, + "%s all new pages must be soft dirty\n", prefix); + + // 2. all pages must not be soft dirty + dirty =3D pagemap_ioctl(mem, mem_size, vec, 1, flags, 0, PAGE_IS_SD, 0, 0= , PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(dirty =3D=3D 0, "%s all pages must not be soft dirty\n",= prefix); + + // 3. all pages dirty other than first and the last one + memset(mem + page_size, -1, mem_size - (2 * page_size)); + + dirty =3D pagemap_ioctl(mem, mem_size, vec, 1, flags, 0, PAGE_IS_SD, 0, 0= , PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(dirty =3D=3D 1 && vec[0].len >=3D vec_size - 2 && vec[0]= .len <=3D vec_size, + "%s all pages dirty other than first and the last one\n", prefix); + + // 4. only middle page dirty + clear_softdirty(); + mem[vec_size/2 * page_size]++; + + dirty =3D pagemap_ioctl(mem, mem_size, vec, vec_size, flags, 0, PAGE_IS_S= D, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(vec[0].start =3D=3D (uintptr_t)(mem + vec_size/2 * page_= size), + "%s only middle page dirty\n", prefix); + + // 5. only two middle pages dirty and walk over only middle pages + clear_softdirty(); + mem[vec_size/2 * page_size]++; + mem[(vec_size/2 + 1) * page_size]++; + + dirty =3D pagemap_ioctl(&mem[vec_size/2 * page_size], 2 * page_size, vec,= 1, flags, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(dirty =3D=3D 1 && vec[0].start =3D=3D (uintptr_t)(&mem[v= ec_size/2 * page_size]) && + vec[0].len =3D=3D 2, + "%s only two middle pages dirty\n", prefix); + + /* 6. only get 2 dirty pages and clear them as well */ + memset(mem, -1, mem_size); + + /* get and clear second and third pages */ + ret =3D pagemap_ioctl(mem + page_size, 2 * page_size, vec, 1, flags | PAG= EMAP_SD_CLEAR, 2, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + dirty =3D pagemap_ioctl(mem, mem_size, vec2, vec_size, flags, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len =3D=3D 2 && + vec[0].start =3D=3D (uintptr_t)(mem + page_size) && + dirty =3D=3D 2 && vec2[0].len =3D=3D 1 && vec2[0].start =3D=3D (uintpt= r_t)mem && + vec2[1].len =3D=3D vec_size - 3 && + vec2[1].start =3D=3D (uintptr_t)(mem + 3 * page_size), + "%s only get 2 dirty pages and clear them as well\n", prefix); + + /* 7. Range clear only */ + memset(mem, -1, mem_size); + + dirty =3D pagemap_ioctl(mem, mem_size, NULL, 0, flags | PAGEMAP_SD_CLEAR,= 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + dirty2 =3D pagemap_ioctl(mem, mem_size, vec, vec_size, flags, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty2 < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty2, errno, strerror(errno)); + + ksft_test_result(dirty =3D=3D 0 && dirty2 =3D=3D 0, "%s Range clear only\= n", + prefix); + + free(vec); + free(vec2); + return 0; +} + +void *gethugepage(int map_size) +{ + int ret; + char *map; + size_t hpage_len =3D read_pmd_pagesize(); + + map =3D memalign(hpage_len, map_size); + if (!map) + ksft_exit_fail_msg("memalign failed %d %s\n", errno, strerror(errno)); + + ret =3D madvise(map, map_size, MADV_HUGEPAGE); + if (ret) + ksft_exit_fail_msg("madvise failed %d %d %s\n", ret, errno, strerror(err= no)); + + memset(map, 0, map_size); + + if (check_huge_anon(map, map_size/hpage_len, hpage_len)) + return map; + + free(map); + return NULL; + +} + +int hpage_unit_tests(int page_size) +{ + char *map; + int ret; + size_t hpage_len =3D read_pmd_pagesize(); + size_t num_pages =3D 10; + int map_size =3D hpage_len * num_pages; + int vec_size =3D map_size/page_size; + struct page_region *vec, *vec2; + + vec =3D malloc(sizeof(struct page_region) * vec_size); + vec2 =3D malloc(sizeof(struct page_region) * vec_size); + if (!vec || !vec2) + ksft_exit_fail_msg("malloc failed\n"); + + map =3D gethugepage(map_size); + if (map) { + // 1. all new huge page must be dirty + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].start =3D=3D (uintptr_t)map && + vec[0].len =3D=3D vec_size && vec[0].bitmap =3D=3D PAGE_IS_SD, + "%s all new huge page must be dirty\n", __func__); + + // 2. all the huge page must not be dirty + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 0, "%s all the huge page must not be dirty\n= ", __func__); + + // 3. all the huge page must be dirty and clear dirty as well + memset(map, -1, map_size); + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].start =3D=3D (uintptr_t)map && + vec[0].len =3D=3D vec_size && vec[0].bitmap =3D=3D PAGE_IS_SD, + "%s all the huge page must be dirty and clear\n", __func__); + + // 4. only middle page dirty + free(map); + map =3D gethugepage(map_size); + clear_softdirty(); + map[vec_size/2 * page_size]++; + + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len > 0, + "%s only middle page dirty\n", __func__); + + free(map); + } else { + ksft_test_result_skip("all new huge page must be dirty\n"); + ksft_test_result_skip("all the huge page must not be dirty\n"); + ksft_test_result_skip("all the huge page must be dirty and clear\n"); + ksft_test_result_skip("only middle page dirty\n"); + } + + // 5. clear first half of huge page + map =3D gethugepage(map_size); + if (map) { + ret =3D pagemap_ioctl(map, map_size/2, NULL, 0, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len =3D=3D vec_size/2 && + vec[0].start =3D=3D (uintptr_t)(map + map_size/2), + "%s clear first half of huge page\n", __func__); + free(map); + } else { + ksft_test_result_skip("clear first half of huge page\n"); + } + + // 6. clear first half of huge page with limited buffer + map =3D gethugepage(map_size); + if (map) { + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, PAGEMAP_SD_CLEAR, ve= c_size/2, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len =3D=3D vec_size/2 && + vec[0].start =3D=3D (uintptr_t)(map + map_size/2), + "%s clear first half of huge page with limited buffer\n", + __func__); + + free(map); + } else { + ksft_test_result_skip("clear first half of huge page with limited buffer= \n"); + } + + // 7. clear second half of huge page + map =3D gethugepage(map_size); + if (map) { + memset(map, -1, map_size); + ret =3D pagemap_ioctl(map + map_size/2, map_size, NULL, 0, PAGEMAP_SD_CL= EAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ret =3D pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + ksft_test_result(ret =3D=3D 1 && vec[0].len =3D=3D vec_size/2, + "%s clear second half huge page\n", __func__); + free(map); + } else { + ksft_test_result_skip("clear second half huge page\n"); + } + + free(vec); + free(vec2); + return 0; +} + +int unmapped_region_tests(int page_size) +{ + void *start =3D (void *)0x10000000; + int dirty, len =3D 0x00040000; + int vec_size =3D len / page_size; + struct page_region *vec =3D malloc(sizeof(struct page_region) * vec_size); + + /* 1. Get dirty pages */ + dirty =3D pagemap_ioctl(start, len, vec, vec_size, 0, 0, PAGE_IS_SD, 0, 0= , PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(dirty >=3D 0, "%s Get dirty pages\n", __func__); + + /* 2. Clear dirty bit of whole address space */ + dirty =3D pagemap_ioctl(0, 0x7FFFFFFF, NULL, 0, PAGEMAP_SD_CLEAR, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD); + if (dirty < 0) + ksft_exit_fail_msg("error %d %d %s\n", dirty, errno, strerror(errno)); + + ksft_test_result(dirty =3D=3D 0, "%s Get dirty pages\n", __func__); + + free(vec); + return 0; +} + +static void test_simple(int page_size) +{ + int i; + char *map; + struct page_region vec; + + map =3D aligned_alloc(page_size, page_size); + if (!map) + ksft_exit_fail_msg("mmap failed\n"); + + clear_softdirty(); + + for (i =3D 0 ; i < TEST_ITERATIONS; i++) { + if (pagemap_ioctl(map, page_size, &vec, 1, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD) =3D=3D 1) { + ksft_print_msg("dirty bit was 1, but should be 0 (i=3D%d)\n", i); + break; + } + + clear_softdirty(); + // Write something to the page to get the dirty bit enabled on the page + map[0]++; + + if (pagemap_ioctl(map, page_size, &vec, 1, 0, 0, + PAGE_IS_SD, 0, 0, PAGE_IS_SD) =3D=3D 0) { + ksft_print_msg("dirty bit was 0, but should be 1 (i=3D%d)\n", i); + break; + } + + clear_softdirty(); + } + free(map); + + ksft_test_result(i =3D=3D TEST_ITERATIONS, "Test %s\n", __func__); +} + +int sanity_tests(int page_size) +{ + char *mem, *fmem; + int mem_size, vec_size, ret; + struct page_region *vec; + + /* 1. wrong operation */ + mem_size =3D 10 * page_size; + vec_size =3D mem_size / page_size; + + vec =3D malloc(sizeof(struct page_region) * vec_size); + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem || !vec) + ksft_exit_fail_msg("error nomem\n"); + + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, + PAGEMAP_SD_CLEAR | PAGEMAP_NO_REUSED_REGIONS, 0, + PAGEMAP_OP_MASK, 0, 0, PAGEMAP_OP_MASK) < 0, + "%s clear op can only be specified with PAGE_IS_DIRTY\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + PAGEMAP_OP_MASK, 0, 0, PAGEMAP_OP_MASK) >=3D 0, + "%s rmask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, PAGEMAP_OP_MASK, 0, PAGEMAP_OP_MASK) >=3D 0, + "%s amask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, 0, PAGEMAP_OP_MASK, PAGEMAP_OP_MASK) >=3D 0, + "%s emask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + PAGEMAP_OP_MASK, PAGEMAP_OP_MASK, 0, PAGEMAP_OP_MASK) >=3D 0, + "%s rmask and amask specified\n", __func__); + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, PAGEMAP_SD_C= LEAR, 0, + 0, 0, PAGEMAP_OP_MASK, PAGEMAP_OP_MASK) >=3D 0, + "%s rmask and amask specified\n", __func__); + munmap(mem, mem_size); + + /* 2. Get sd and present pages with amask */ + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem) + ksft_exit_fail_msg("error nomem\n"); + memset(mem, 0, mem_size); + + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, PAGEMAP_OP_MASK, 0, PAGEMAP_OP_MASK); + ksft_test_result(ret >=3D 0 && vec[0].start =3D=3D (uintptr_t)mem && vec[= 0].len =3D=3D vec_size && + vec[0].bitmap =3D=3D (PAGE_IS_SD | PAGE_IS_PRESENT), + "%s Get sd and present pages with amask\n", __func__); + + /* 3. Get sd and present pages with rmask */ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + PAGEMAP_OP_MASK, 0, 0, PAGEMAP_OP_MASK); + ksft_test_result(ret >=3D 0 && vec[0].start =3D=3D (uintptr_t)mem && vec[= 0].len =3D=3D vec_size && + vec[0].bitmap =3D=3D (PAGE_IS_SD | PAGE_IS_PRESENT), + "%s Get all the pages with rmask\n", __func__); + + /* 4. Get sd and present pages with rmask and amask */ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + PAGE_IS_SD, PAGE_IS_PRESENT, 0, PAGEMAP_OP_MASK); + ksft_test_result(ret >=3D 0 && vec[0].start =3D=3D (uintptr_t)mem && vec[= 0].len =3D=3D vec_size && + vec[0].bitmap =3D=3D (PAGE_IS_SD | PAGE_IS_PRESENT), + "%s Get sd and present pages with rmask and amask\n", __func__); + + /* 5. Don't get sd pages */ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, 0, PAGE_IS_SD, PAGEMAP_OP_MASK); + ksft_test_result(ret =3D=3D 0, "%s Don't get sd pages\n", __func__); + + /* 6. Don't get present pages */ + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, 0, PAGE_IS_PRESENT, PAGEMAP_OP_MASK); + ksft_test_result(ret =3D=3D 0, "%s Don't get present pages\n", __func__); + + munmap(mem, mem_size); + + /* 8. Find dirty present pages with return mask */ + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem) + ksft_exit_fail_msg("error nomem\n"); + memset(mem, 0, mem_size); + + ret =3D pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, + 0, PAGEMAP_OP_MASK, 0, PAGE_IS_SD); + ksft_test_result(ret >=3D 0 && vec[0].start =3D=3D (uintptr_t)mem && vec[= 0].len =3D=3D vec_size && + vec[0].bitmap =3D=3D PAGE_IS_SD, + "%s Find dirty present pages with return mask\n", __func__); + + /* 9. Memory mapped file */ + int fd; + struct stat sbuf; + + fd =3D open("run_vmtests.sh", O_RDONLY); + if (fd < 0) { + ksft_test_result_skip("%s Memory mapped file\n"); + goto free_vec_and_return; + } + + ret =3D stat("run_vmtests.sh", &sbuf); + if (ret < 0) + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); + + fmem =3D mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (!fmem) + ksft_exit_fail_msg("error nomem\n"); + + ret =3D pagemap_ioctl(fmem, sbuf.st_size, vec, vec_size, 0, 0, + 0, PAGEMAP_OP_MASK, 0, PAGEMAP_OP_MASK); + + ksft_test_result(ret >=3D 0 && vec[0].start =3D=3D (uintptr_t)fmem && + vec[0].len =3D=3D ceilf((float)sbuf.st_size/page_size) && + vec[0].bitmap =3D=3D (PAGE_IS_SD | PAGE_IS_FILE), + "%s Memory mapped file\n", __func__); + + munmap(fmem, sbuf.st_size); + +free_vec_and_return: + free(vec); + return 0; +} + +int main(void) +{ + int page_size =3D getpagesize(); + size_t hpage_len =3D read_pmd_pagesize(); + char *mem, *map; + int mem_size; + + ksft_print_header(); + ksft_set_plan(59); + + pagemap_fd =3D open(PAGEMAP, O_RDWR); + if (pagemap_fd < 0) + return -EINVAL; + + /* + * Soft-dirty PTE bit tests + */ + + /* 1. Sanity testing */ + sanity_tests_sd(page_size); + + /* 2. Normal page testing */ + mem_size =3D 10 * page_size; + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem) + ksft_exit_fail_msg("error nomem\n"); + + base_tests("Page testing:", mem, mem_size, page_size, 0, 0); + + munmap(mem, mem_size); + + /* 3. Large page testing */ + mem_size =3D 512 * 10 * page_size; + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem) + ksft_exit_fail_msg("error nomem\n"); + + base_tests("Large Page testing:", mem, mem_size, page_size, 0, 0); + + munmap(mem, mem_size); + + /* 4. Huge page testing */ + map =3D gethugepage(hpage_len); + if (map) + base_tests("Huge page testing:", map, hpage_len, page_size, 0, 0); + else + base_tests("Huge page testing:", NULL, 0, 0, 1, 0); + + free(map); + + /* 5. Performance page testing */ + mem_size =3D 10 * page_size; + mem =3D mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_AN= ON, -1, 0); + if (!mem) + ksft_exit_fail_msg("error nomem\n"); + + base_tests("Performance Page testing:", mem, mem_size, page_size, 0, + PAGEMAP_NO_REUSED_REGIONS); + + munmap(mem, mem_size); + + /* 6. Huge page tests */ + hpage_unit_tests(page_size); + + /* 7. Unmapped address test */ + unmapped_region_tests(page_size); + + /* 8. Iterative test */ + test_simple(page_size); + + /* + * Other PTE bit tests + */ + + /* 1. Sanity testing */ + sanity_tests(page_size); + + close(pagemap_fd); + return ksft_exit_pass(); +} --=20 2.30.2