From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=95.215.58.72 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794308850; b=TXt+Tgscqvm4nNoWpkjO6/nqM4Jf/KUcOdT6V4J043tesC3A9Mp+Y/gOyZR8THuBw9U3 olayXGxuVWTa4zEtAVhnAtbazhyXDiNhCZw96JVWnVIXxE00BO/0lM/nC/3THerSDMAzo b90SRZqCoASnJskOlq9gM4fSaLe5wfHi65Xf0xE4naE+wVoSkDY0nB9/5lhKb25GEURQw L4xobpe3znXnVBap0FCiPFdHOsi55l07lw0u5Th9qK/dtAANwpGnmnPkR8+KkFuYEqQf8 O4GXzqR/n68NqZeKSA+KJtlxYYdMbHImboS8NraILeBDzBgs78yCc07BAW/fqocxTrA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794308850; h=DMARC-Filter:X-Envelope-To:DKIM-Signature:X-Envelope-To:Received: X-Mizu-Trace-ID:X-Migadu-Flow:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=Gk5/z5UHtpWXsmW1+RWmjFtC0T17jkciMDqpUKmwIl8=; b=aHg+0HfZttOv+nQCmK9wt2GZ7smW8aJ/GwfV705/+YHjopQmZy/U6dCeJoVcx0sgoA+4 v1wKlB1oJCnmfSHeF38eieyocXjQ+cqvQu4SNJQRCygl1Tdi/exVrXDkTfjzq2AluI+HB 7wX11b3YOhozCcqoLht2T04ml4jtJn2kr7U83K8qXsqBPcd9+uH5dZrDf5LAPBllH+9Dx rEkevtIIUHAdgPma7p2ON4n3S/898RWUkfot4tt/bDkmNd3+Iq5IoCi9w1GlK0M/fy7n9 RbVIJZEFWNCFUrkshWWA3uFadH/6Q5YqXXpilzQwDasesJuT67eF3O+vTZQmYUf6x+A== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=qXfWzAa+; arc=none smtp.remote-ip=95.215.58.72 Received: from mta1.migadu.com (out-72.mta1.migadu.com [95.215.58.72]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60883606A326 for ; Tue, 10 Nov 2026 11:07:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 60883606A326 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=qXfWzAa+ X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=BXrmF/fjfe9y8XP2JkR7Eemnuq82V/mUf87aDFH49v8=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1794308847; v=1; x=1794913647; b=qXfWzAa+8dZDdq/ERMCS5bJOt9rypRbyzZpB+ChIlRsChLLryno7WD31lF5ziUkr2o6Bw9mn p5Pcqb/eM+mW0b3w+/Q7D30FKgLAGjg4iUIdnWpdePz93Iz7lRyf5uVWEiUVVfX/x4EnjaOhooJ lZ8lICA/WTSYDgXQHe04wJGA= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 6ea7a5a5c9741bf1; Tue, 10 Nov 2026 11:07:27 +0000 X-Mizu-Trace-ID: 6ea7a5a5c9741bf1 X-Migadu-Flow: FLOW_OUT From: Fuad Tabba To: Paolo Bonzini , Sean Christopherson Cc: kvm@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Jonathan Corbet , Shuah Khan , Isaku Yamahata , Rick Edgecombe , Lorenzo Stoakes , Marc Zyngier , Oliver Upton , kvmarm@lists.linux.dev, Wei-Lin Chang , Will Deacon , Fuad Tabba , stable@vger.kernel.org Subject: [PATCH 1/2] KVM: Stop KVM_PRE_FAULT_MEMORY when the vCPU's dirty ring is full Date: Tue, 10 Nov 2026 11:07:23 +0000 Message-ID: <20261110110724.3614412-2-fuad.tabba@linux.dev> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261110110724.3614412-1-fuad.tabba@linux.dev> References: <20261110110724.3614412-1-fuad.tabba@linux.dev> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Pre-faulting can map a dirty-logged page writable, which pushes it to the vCPU's dirty ring. KVM_RUN exits with KVM_EXIT_DIRTY_RING_FULL before the ring overflows, but the pre-fault loop never checks it, so one call over more such pages than the ring has free trips the WARN in kvm_dirty_ring_push() and overwrites entries userspace hasn't harvested. On x86: WARNING: arch/x86/kvm/../../../virt/kvm/dirty_ring.c:225 at kvm_dirty_ring_push+0x7a/0xd0 Call Trace: make_spte+0x2cc/0x450 kvm_tdp_mmu_map+0x5e1/0x7f0 kvm_tdp_page_fault+0x12c/0x1b0 kvm_mmu_do_page_fault+0x181/0x250 kvm_arch_vcpu_pre_fault_memory+0x136/0x1d0 kvm_vcpu_pre_fault_memory+0xcf/0x180 kvm_vcpu_ioctl+0x455/0x770 Stop the loop once the ring is soft full, returning the pages already processed, or -ENOSPC when there are none, since a retry can't succeed until userspace harvests the ring. kvm_dirty_ring_check_request() fills in a KVM_RUN exit, so test the ring's state directly. The stub for !HAVE_KVM_DIRTY_RING is for s390, which has the ioctl but no ring. Fixes: bc1a5cd002116 ("KVM: Add KVM_PRE_FAULT_MEMORY vcpu ioctl to pre-populate guest memory") Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba --- Documentation/virt/kvm/api.rst | 9 +++++++-- include/linux/kvm_dirty_ring.h | 6 ++++++ virt/kvm/dirty_ring.c | 2 +- virt/kvm/kvm_main.c | 6 ++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index e95c9cda085d9..89eb6be9bc6e3 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6544,6 +6544,7 @@ Errors: ENOENT The specified `gpa` is outside defined memslots. ENOEXEC The vCPU has not been initialised (arm64). EINTR An unmasked signal is pending and no page was processed. + ENOSPC The vCPU's dirty ring is full and no page was processed. EFAULT The parameter address was invalid. EHWPOISON A poisoned host page was encountered. EOPNOTSUPP Mapping memory for a GPA is unsupported by the @@ -6588,7 +6589,9 @@ case, the ioctl can be called in parallel. When the ioctl returns, the input values are updated to point to the remaining range. If `size` > 0 on return, the caller can just issue -the ioctl again with the same `struct kvm_map_memory` argument. +the ioctl again with the same `struct kvm_pre_fault_memory` argument. +If that fails with ENOSPC, the vCPU's dirty ring has to be harvested +and reset first. On x86, shadow page tables cannot support this ioctl because they are indexed by virtual address or nested guest physical address. @@ -8957,7 +8960,9 @@ Therefore, the ioctl must be called *before* reading the content of the dirty pages. The dirty ring can get full. When it happens, the KVM_RUN of the -vcpu will return with exit reason KVM_EXIT_DIRTY_RING_FULL. +vcpu will return with exit reason KVM_EXIT_DIRTY_RING_FULL, and +KVM_PRE_FAULT_MEMORY on the vcpu will process no more pages until the +ring has been harvested and reset. The dirty ring interface has a major difference comparing to the KVM_GET_DIRTY_LOG interface in that, when reading the dirty ring from diff --git a/include/linux/kvm_dirty_ring.h b/include/linux/kvm_dirty_ring.h index eb10d87adf7d5..88b4f2a009274 100644 --- a/include/linux/kvm_dirty_ring.h +++ b/include/linux/kvm_dirty_ring.h @@ -60,6 +60,11 @@ static inline void kvm_dirty_ring_push(struct kvm_vcpu *vcpu, { } +static inline bool kvm_dirty_ring_soft_full(struct kvm_dirty_ring *ring) +{ + return false; +} + static inline struct page *kvm_dirty_ring_get_page(struct kvm_dirty_ring *ring, u32 offset) { @@ -81,6 +86,7 @@ int kvm_dirty_ring_alloc(struct kvm *kvm, struct kvm_dirty_ring *ring, int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring, int *nr_entries_reset); void kvm_dirty_ring_push(struct kvm_vcpu *vcpu, u32 slot, u64 offset); +bool kvm_dirty_ring_soft_full(struct kvm_dirty_ring *ring); bool kvm_dirty_ring_check_request(struct kvm_vcpu *vcpu); diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c index 572b854edf740..f73f92102cb7a 100644 --- a/virt/kvm/dirty_ring.c +++ b/virt/kvm/dirty_ring.c @@ -40,7 +40,7 @@ static u32 kvm_dirty_ring_used(struct kvm_dirty_ring *ring) return READ_ONCE(ring->dirty_index) - READ_ONCE(ring->reset_index); } -static bool kvm_dirty_ring_soft_full(struct kvm_dirty_ring *ring) +bool kvm_dirty_ring_soft_full(struct kvm_dirty_ring *ring) { return kvm_dirty_ring_used(ring) >= ring->soft_limit; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index cf4cb586f1407..da1b76ceb65e3 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4461,6 +4461,12 @@ static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, break; } + /* Like KVM_RUN: only the iteration crossing the soft limit uses the reserve. */ + if (vcpu->kvm->dirty_ring_size && kvm_dirty_ring_soft_full(&vcpu->dirty_ring)) { + r = -ENOSPC; + break; + } + r = kvm_arch_vcpu_pre_fault_memory(vcpu, range); if (WARN_ON_ONCE(r == 0 || r == -EIO)) break; -- 2.47.3