Re: [PATCH v1] KVM: s390x: selftests: Add shared zeropage test

From: Muhammad Usama Anjum
Date: Mon Apr 15 2024 - 14:43:02 EST


On 4/12/24 1:43 PM, David Hildenbrand wrote:
> Let's test that we can have shared zeropages in our process as long as
> storage keys are not getting used, that shared zeropages are properly
> unshared (replaced by anonymous pages) once storage keys are enabled,
> and that no new shared zeropages are populated after storage keys
> were enabled.
>
> We require the new pagemap interface to detect the shared zeropage.
>
> On an old kernel (zeropages always disabled):
> # ./s390x/shared_zeropage_test
> TAP version 13
> 1..3
> not ok 1 Shared zeropages should be enabled
> ok 2 Shared zeropage should be gone
> ok 3 Shared zeropages should be disabled
> # Totals: pass:2 fail:1 xfail:0 xpass:0 skip:0 error:0
>
> On a fixed kernel:
> # ./s390x/shared_zeropage_test
> TAP version 13
> 1..3
> ok 1 Shared zeropages should be enabled
> ok 2 Shared zeropage should be gone
> ok 3 Shared zeropages should be disabled
> # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
>
> Testing of UFFDIO_ZEROPAGE can be added later.
>
> Cc: Christian Borntraeger <borntraeger@xxxxxxxxxxxxx>
> Cc: Janosch Frank <frankja@xxxxxxxxxxxxx>
> Cc: Claudio Imbrenda <imbrenda@xxxxxxxxxxxxx>
> Cc: Thomas Huth <thuth@xxxxxxxxxx>
> Cc: Alexander Gordeev <agordeev@xxxxxxxxxxxxx>
> Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> Cc: Shuah Khan <shuah@xxxxxxxxxx>
> Signed-off-by: David Hildenbrand <david@xxxxxxxxxx>
Acked-by: Muhammad Usama Anjum <usama.anjum@xxxxxxxxxxxxx>

> ---
>
> To get it right this time, test the relevant cases.
>
> v3 of fixes are at:
> https://lore.kernel.org/all/20240411161441.910170-1-david@xxxxxxxxxx/T/#u
>
> ---
> tools/testing/selftests/kvm/Makefile | 1 +
> .../kvm/s390x/shared_zeropage_test.c | 110 ++++++++++++++++++
> 2 files changed, 111 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/s390x/shared_zeropage_test.c
>
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 741c7dc16afc..ed4ad591f193 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -180,6 +180,7 @@ TEST_GEN_PROGS_s390x += s390x/sync_regs_test
> TEST_GEN_PROGS_s390x += s390x/tprot
> TEST_GEN_PROGS_s390x += s390x/cmma_test
> TEST_GEN_PROGS_s390x += s390x/debug_test
> +TEST_GEN_PROGS_s390x += s390x/shared_zeropage_test
> TEST_GEN_PROGS_s390x += demand_paging_test
> TEST_GEN_PROGS_s390x += dirty_log_test
> TEST_GEN_PROGS_s390x += guest_print_test
> diff --git a/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c
> new file mode 100644
> index 000000000000..74e829748fb1
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c
> @@ -0,0 +1,110 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Test shared zeropage handling (with/without storage keys)
> + *
> + * Copyright (C) 2024, Red Hat, Inc.
> + */
> +#include <sys/mman.h>
> +
> +#include <linux/fs.h>
> +
> +#include "test_util.h"
> +#include "kvm_util.h"
> +#include "kselftest.h"
> +
> +static void set_storage_key(void *addr, uint8_t skey)
> +{
> + asm volatile("sske %0,%1" : : "d" (skey), "a" (addr));
> +}
> +
> +static void guest_code(void)
> +{
> + /* Issue some storage key instruction. */
> + set_storage_key((void *)0, 0x98);
> + GUEST_DONE();
> +}
> +
> +/*
> + * Returns 1 if the shared zeropage is mapped, 0 if something else is mapped.
> + * Returns < 0 on error or if nothing is mapped.
> + */
> +static int maps_shared_zeropage(int pagemap_fd, void *addr)
> +{
> + struct page_region region;
> + struct pm_scan_arg arg = {
> + .start = (uintptr_t)addr,
> + .end = (uintptr_t)addr + 4096,
> + .vec = (uintptr_t)&region,
> + .vec_len = 1,
> + .size = sizeof(struct pm_scan_arg),
> + .category_mask = PAGE_IS_PFNZERO,
> + .category_anyof_mask = PAGE_IS_PRESENT,
> + .return_mask = PAGE_IS_PFNZERO,
> + };
> + return ioctl(pagemap_fd, PAGEMAP_SCAN, &arg);Its good to see more users for it.

> +}
> +
> +int main(int argc, char *argv[])
> +{
> + char *mem, *page0, *page1, *page2, tmp;
> + const size_t pagesize = getpagesize();
> + struct kvm_vcpu *vcpu;
> + struct kvm_vm *vm;
> + struct ucall uc;
> + int pagemap_fd;
> +
> + ksft_print_header();
> + ksft_set_plan(3);
> +
> + /*
> + * We'll use memory that is not mapped into the VM for simplicity.
> + * Shared zeropages are enabled/disabled per-process.
> + */
> + mem = mmap(0, 3 * pagesize, PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0);
> + TEST_ASSERT(mem != MAP_FAILED, "mmap() failed");
> +
> + /* Disable THP. Ignore errors on older kernels. */
> + madvise(mem, 3 * pagesize, MADV_NOHUGEPAGE);
> +
> + page0 = mem;
> + page1 = page0 + pagesize;
> + page2 = page1 + pagesize;
> +
> + /* Can we even detect shared zeropages? */
> + pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
> + TEST_REQUIRE(pagemap_fd >= 0);
> +
> + tmp = *page0;
> + asm volatile("" : "+r" (tmp));
> + TEST_REQUIRE(maps_shared_zeropage(pagemap_fd, page0) == 1);
> +
> + vm = vm_create_with_one_vcpu(&vcpu, guest_code);
> +
> + /* Verify that we get the shared zeropage after VM creation. */
> + tmp = *page1;
> + asm volatile("" : "+r" (tmp));
> + ksft_test_result(maps_shared_zeropage(pagemap_fd, page1) == 1,
> + "Shared zeropages should be enabled\n");
> +
> + /*
> + * Let our VM execute a storage key instruction that should
> + * unshare all shared zeropages.
> + */
> + vcpu_run(vcpu);
> + get_ucall(vcpu, &uc);
> + TEST_ASSERT_EQ(uc.cmd, UCALL_DONE);
> +
> + /* Verify that we don't have a shared zeropage anymore. */
> + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page1),
> + "Shared zeropage should be gone\n");
> +
> + /* Verify that we don't get any new shared zeropages. */
> + tmp = *page2;
> + asm volatile("" : "+r" (tmp));
> + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page2),
> + "Shared zeropages should be disabled\n");
> +
> + kvm_vm_free(vm);
> +
> + ksft_finished();
> +}

--
BR,
Muhammad Usama Anjum