From nobody Mon Feb 9 13:36:07 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D92F9341650 for ; Thu, 16 Oct 2025 17:30:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760635830; cv=none; b=J2JvXmg+CRV2Toa18QQ4Hfc5npykwYU764A+j4/OqiWiaZowUrUyPYVeNr6W0d4pi98vRWcI1RMA9RWxKF06Sc1SPkDo1wz4rsKCHCnCz7v8vbMPG5rk5AbCRw/yMVaRCGJC4HGaGgzl/na8v2Y9rlmDUVh1yk1PNEMVVbDwV7w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760635830; c=relaxed/simple; bh=C2oacOVaYmakYuuj01sv0kUdLX8KmQYOY8HvA7F3pWg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pcMRjbdo12WF5jj0Ec/u8g2YhFlkDPP1TEJO3m9Z+DgO/WsfUT0bnkST7r68/k5SHtoX2qYm8SHgVhpp5hp2Ek+IPh6Dp2HyRb5WztF2jGtar1k/KraIUCAHzCnzSdTFxm7xvGP8XVWMaIXz0H0Vythou8AE9DQEa0M4A+zMKEQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Auifua72; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Auifua72" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-290bd7c835dso6032245ad.3 for ; Thu, 16 Oct 2025 10:30:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1760635828; x=1761240628; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=RviFSJTeDXrAnxp03nizg0uEL1EB4BJAqQICQBGlk4U=; b=Auifua72X2vwTv8JgEizsVVJvaoer5KZqGa2aNgCC/T3MtIb+oCxPCtyluUAjlArtS +p12hQWkKaaSZ5/BMzFbF3ZiholHdVKnm12X0vghNCD2JsAXJTl0z9cL+rUVboG+ypHN lKmvKw5ZqQ7wr+oZ7bKUcestkswKaJL5yfsFdsJvPUdlpESImGV6vcqxxO7mQRo/rSey lvtYm9RyVTjPUsP6DrY88KGqC5ExMlfC4+FFqQXcqiD9+UoPjPdK6RQGdyU2vdOmbQ8R eaoHrZ4wX+Ods4086+uwN3zvoo0BGg7XIAQMs5EADZbe95mjPP2Mqiu/hRc+uJKoPzNZ Q31Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760635828; x=1761240628; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=RviFSJTeDXrAnxp03nizg0uEL1EB4BJAqQICQBGlk4U=; b=jBYvoD3/AxFPvD69ZE1kC/5179/OwMDoDcRHJAA4olOVfClGRRk1NG0/AA3tVJ+URI L/TWzvBSkOywtzrS3s6o/gAB8zg0zyEr7vwbDy/GfuYGvjCfDAyYNgLRaxyFM/n8wijH XqrYbLCUnEV63D5RDL6huAnkkaqQYlYX3jcSwK+LMWCHkkI9QaL/Ypz8QlpmBBs6Rj2X 9TlFdqusW8+4jpqKYpyAWlflUyvTY6ImNfw1CwZ2QTkrFuOx8BWgI8XgnsX8xrECNPMQ g8lSeeXUScdP828uRfHvLcFFo9YXQcAfpUBi/siupAcyfNLBAsVVALBN4RH5L78H0gAH 4jqA== X-Forwarded-Encrypted: i=1; AJvYcCWRanpqZn3Mu7RnGm3eoyBETSVDVvDH8FZ/zOrq/fhNBl2p3mi4IIXdgS1Y4y+JCGyhRM3L7GQUq2zINk4=@vger.kernel.org X-Gm-Message-State: AOJu0YyMvCPyIvo1vMOtXFjqs+YG1hhDocr73wFtzA6PLRd2taxTJmkw iJW1URStt+IIM0HZXuw05bbxi9/RJaur2Iip0axqAWOkFsC54d+9VoEzihuGVbSB8LBQxg5+1WV BDQMEaQ== X-Google-Smtp-Source: AGHT+IF/2wryDBFn3Vm/ZItHrOO8Cutyh10iJkyo92xaEeL3XVP9OxJeQvoekCLXXPfR84HIRIYnmu6+FJY= X-Received: from plsq3.prod.google.com ([2002:a17:902:bd83:b0:27e:ec80:30c6]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:b8b:b0:24c:ed95:2725 with SMTP id d9443c01a7336-290c9c89badmr7138805ad.4.1760635828021; Thu, 16 Oct 2025 10:30:28 -0700 (PDT) Reply-To: Sean Christopherson Date: Thu, 16 Oct 2025 10:28:52 -0700 In-Reply-To: <20251016172853.52451-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251016172853.52451-1-seanjc@google.com> X-Mailer: git-send-email 2.51.0.858.gf9c4a03a3a-goog Message-ID: <20251016172853.52451-12-seanjc@google.com> Subject: [PATCH v13 11/12] KVM: selftests: Add guest_memfd tests for mmap and NUMA policy support From: Sean Christopherson To: Miguel Ojeda , Marc Zyngier , Oliver Upton , Paolo Bonzini , Sean Christopherson Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Ackerley Tng , Shivank Garg , David Hildenbrand , Fuad Tabba , Ashish Kalra , Vlastimil Babka Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Shivank Garg Add tests for NUMA memory policy binding and NUMA aware allocation in guest_memfd. This extends the existing selftests by adding proper validation for: - KVM GMEM set_policy and get_policy() vm_ops functionality using mbind() and get_mempolicy() - NUMA policy application before and after memory allocation Run the NUMA mbind() test with and without INIT_SHARED, as KVM should allow doing mbind(), madvise(), etc. on guest-private memory, e.g. so that userspace can set NUMA policy for CoCo VMs. Run the NUMA allocation test only for INIT_SHARED, i.e. if the host can't fault-in memory (via direct access, madvise(), etc.) as move_pages() returns -ENOENT if the page hasn't been faulted in (walks the host page tables to find the associated folio) [sean: don't skip entire test when running on non-NUMA system, test mbind() with private memory, provide more info in assert messages] Signed-off-by: Shivank Garg Tested-by: Ashish Kalra Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/guest_memfd_test.c | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing= /selftests/kvm/guest_memfd_test.c index e7d9aeb418d3..618c937f3c90 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -19,6 +19,7 @@ #include =20 #include "kvm_util.h" +#include "numaif.h" #include "test_util.h" #include "ucall_common.h" =20 @@ -75,6 +76,101 @@ static void test_mmap_supported(int fd, size_t total_si= ze) kvm_munmap(mem, total_size); } =20 +static void test_mbind(int fd, size_t total_size) +{ + const unsigned long nodemask_0 =3D 1; /* nid: 0 */ + unsigned long nodemask =3D 0; + unsigned long maxnode =3D 8; + int policy; + char *mem; + int ret; + + if (!is_multi_numa_node_system()) + return; + + mem =3D kvm_mmap(total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd); + + /* Test MPOL_INTERLEAVE policy */ + kvm_mbind(mem, page_size * 2, MPOL_INTERLEAVE, &nodemask_0, maxnode, 0); + kvm_get_mempolicy(&policy, &nodemask, maxnode, mem, MPOL_F_ADDR); + TEST_ASSERT(policy =3D=3D MPOL_INTERLEAVE && nodemask =3D=3D nodemask_0, + "Wanted MPOL_INTERLEAVE (%u) and nodemask 0x%lx, got %u and 0x%lx", + MPOL_INTERLEAVE, nodemask_0, policy, nodemask); + + /* Test basic MPOL_BIND policy */ + kvm_mbind(mem + page_size * 2, page_size * 2, MPOL_BIND, &nodemask_0, max= node, 0); + kvm_get_mempolicy(&policy, &nodemask, maxnode, mem + page_size * 2, MPOL_= F_ADDR); + TEST_ASSERT(policy =3D=3D MPOL_BIND && nodemask =3D=3D nodemask_0, + "Wanted MPOL_BIND (%u) and nodemask 0x%lx, got %u and 0x%lx", + MPOL_BIND, nodemask_0, policy, nodemask); + + /* Test MPOL_DEFAULT policy */ + kvm_mbind(mem, total_size, MPOL_DEFAULT, NULL, 0, 0); + kvm_get_mempolicy(&policy, &nodemask, maxnode, mem, MPOL_F_ADDR); + TEST_ASSERT(policy =3D=3D MPOL_DEFAULT && !nodemask, + "Wanted MPOL_DEFAULT (%u) and nodemask 0x0, got %u and 0x%lx", + MPOL_DEFAULT, policy, nodemask); + + /* Test with invalid policy */ + ret =3D mbind(mem, page_size, 999, &nodemask_0, maxnode, 0); + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D EINVAL, + "mbind with invalid policy should fail with EINVAL"); + + kvm_munmap(mem, total_size); +} + +static void test_numa_allocation(int fd, size_t total_size) +{ + unsigned long node0_mask =3D 1; /* Node 0 */ + unsigned long node1_mask =3D 2; /* Node 1 */ + unsigned long maxnode =3D 8; + void *pages[4]; + int status[4]; + char *mem; + int i; + + if (!is_multi_numa_node_system()) + return; + + mem =3D kvm_mmap(total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd); + + for (i =3D 0; i < 4; i++) + pages[i] =3D (char *)mem + page_size * i; + + /* Set NUMA policy after allocation */ + memset(mem, 0xaa, page_size); + kvm_mbind(pages[0], page_size, MPOL_BIND, &node0_mask, maxnode, 0); + kvm_fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, page_siz= e); + + /* Set NUMA policy before allocation */ + kvm_mbind(pages[0], page_size * 2, MPOL_BIND, &node1_mask, maxnode, 0); + kvm_mbind(pages[2], page_size * 2, MPOL_BIND, &node0_mask, maxnode, 0); + memset(mem, 0xaa, total_size); + + /* Validate if pages are allocated on specified NUMA nodes */ + kvm_move_pages(0, 4, pages, NULL, status, 0); + TEST_ASSERT(status[0] =3D=3D 1, "Expected page 0 on node 1, got it on nod= e %d", status[0]); + TEST_ASSERT(status[1] =3D=3D 1, "Expected page 1 on node 1, got it on nod= e %d", status[1]); + TEST_ASSERT(status[2] =3D=3D 0, "Expected page 2 on node 0, got it on nod= e %d", status[2]); + TEST_ASSERT(status[3] =3D=3D 0, "Expected page 3 on node 0, got it on nod= e %d", status[3]); + + /* Punch hole for all pages */ + kvm_fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, total_si= ze); + + /* Change NUMA policy nodes and reallocate */ + kvm_mbind(pages[0], page_size * 2, MPOL_BIND, &node0_mask, maxnode, 0); + kvm_mbind(pages[2], page_size * 2, MPOL_BIND, &node1_mask, maxnode, 0); + memset(mem, 0xaa, total_size); + + kvm_move_pages(0, 4, pages, NULL, status, 0); + TEST_ASSERT(status[0] =3D=3D 0, "Expected page 0 on node 0, got it on nod= e %d", status[0]); + TEST_ASSERT(status[1] =3D=3D 0, "Expected page 1 on node 0, got it on nod= e %d", status[1]); + TEST_ASSERT(status[2] =3D=3D 1, "Expected page 2 on node 1, got it on nod= e %d", status[2]); + TEST_ASSERT(status[3] =3D=3D 1, "Expected page 3 on node 1, got it on nod= e %d", status[3]); + + kvm_munmap(mem, total_size); +} + static void test_fault_sigbus(int fd, size_t accessible_size, size_t map_s= ize) { const char val =3D 0xaa; @@ -273,11 +369,13 @@ static void __test_guest_memfd(struct kvm_vm *vm, uin= t64_t flags) if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) { gmem_test(mmap_supported, vm, flags); gmem_test(fault_overflow, vm, flags); + gmem_test(numa_allocation, vm, flags); } else { gmem_test(fault_private, vm, flags); } =20 gmem_test(mmap_cow, vm, flags); + gmem_test(mbind, vm, flags); } else { gmem_test(mmap_not_supported, vm, flags); } --=20 2.51.0.858.gf9c4a03a3a-goog