Re: [PATCH v8 39/46] KVM: selftests: Test conversion with elevated page refcount
From: Fuad Tabba
Date: Thu Jun 25 2026 - 04:06:49 EST
On Fri, 19 Jun 2026 at 01:32, Ackerley Tng via B4 Relay
<devnull+ackerleytng.google.com@xxxxxxxxxx> wrote:
>
> From: Ackerley Tng <ackerleytng@xxxxxxxxxx>
>
> Add a selftest to verify that converting a shared guest_memfd page to a
> private page fails if the page has an elevated reference count.
>
> When KVM converts a shared page to a private one, it expects the page to
> have a reference count equal to the reference counts taken by the
> filemap. If another kernel subsystem holds a reference to the page, the
> conversion must be aborted.
>
> The test asserts that both bulk and single-page conversion attempts
> correctly fail with EAGAIN for the pinned page. After the page is unpinned,
> the test verifies that subsequent conversions succeed.
>
> Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
> Co-developed-by: Sean Christopherson <seanjc@xxxxxxxxxx>
> Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Not sure Sashiko's concern is worth it.
Reviewed-by: Fuad Tabba <tabba@xxxxxxxxxx>
Cheers,
/fuad
> ---
> .../kvm/x86/guest_memfd_conversions_test.c | 56 ++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
> index 99b0023609670..4ebbd29029526 100644
> --- a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
> +++ b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
> @@ -441,6 +441,62 @@ GMEM_CONVERSION_TEST_INIT_SHARED(forked_accesses)
> #undef TEST_STATE_AWAIT
> }
>
> +static void test_convert_to_private_fails(test_data_t *t, u64 pgoff,
> + size_t nr_pages,
> + u64 expected_error_offset)
> +{
> + /* +1 to make it anything but expected_error_offset. */
> + u64 error_offset = expected_error_offset + 1;
> + u64 offset = pgoff * page_size;
> + int ret;
> +
> + do {
> + ret = __gmem_set_private(t->gmem_fd, offset,
> + nr_pages * page_size, &error_offset);
> + } while (ret == -1 && errno == EINTR);
> + TEST_ASSERT(ret == -1 && errno == EAGAIN,
> + "Wanted EAGAIN on page %lu, got %d (ret = %d)", pgoff,
> + errno, ret);
> + TEST_ASSERT_EQ(error_offset, expected_error_offset);
> +}
> +
> +GMEM_CONVERSION_MULTIPAGE_TEST_INIT_SHARED(elevated_refcount, 4)
> +{
> + int i;
> +
> + pin_pages(t->mem + test_page * page_size, page_size);
> +
> + for (i = 0; i < nr_pages; i++)
> + test_shared(t, i, 0, 'A', 'B');
> +
> + /*
> + * Converting in bulk should fail as long any page in the range has
> + * unexpected refcounts.
> + */
> + test_convert_to_private_fails(t, 0, nr_pages, test_page * page_size);
> +
> + for (i = 0; i < nr_pages; i++) {
> + /*
> + * Converting page-wise should also fail as long any page in the
> + * range has unexpected refcounts.
> + */
> + if (i == test_page)
> + test_convert_to_private_fails(t, i, 1, test_page * page_size);
> + else
> + test_convert_to_private(t, i, 'B', 'C');
> + }
> +
> + unpin_pages();
> +
> + gmem_set_private(t->gmem_fd, 0, nr_pages * page_size);
> +
> + for (i = 0; i < nr_pages; i++) {
> + char expected = i == test_page ? 'B' : 'C';
> +
> + test_private(t, i, expected, 'D');
> + }
> +}
> +
> int main(int argc, char *argv[])
> {
> TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM));
>
> --
> 2.55.0.rc0.738.g0c8ab3ebcc-goog
>
>