From: "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org>
To: linux-coco@lists.linux.dev
Cc: "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org>,
Ackerley Tng <ackerleytng@google.com>,
David Woodhouse <dwmw2@infradead.org>,
David Hildenbrand <david@kernel.org>,
Jason Gunthorpe <jgg@ziepe.ca>, Kevin Tian <kevin.tian@intel.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Sean Christopherson <seanjc@google.com>,
Alexey Kardashevskiy <aik@amd.com>,
Xu Yilun <yilun.xu@linux.intel.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Steven Price <steven.price@arm.com>,
Fred Griffoul <griffoul@gmail.com>,
iommu@lists.linux.dev, kvm@vger.kernel.org,
patches@lists.linux.dev
Subject: [RFC PATCH v5 07/16] KVM: guest_memfd: Release and revoke memory through provider operations
Date: Thu, 12 Nov 2026 13:11:11 +0530 [thread overview]
Message-ID: <20261112074120.1570072-8-aneesh.kumar@kernel.org> (raw)
In-Reply-To: <20261112074120.1570072-1-aneesh.kumar@kernel.org>
Add release_range() helpers for folios and PFNs. Keep guest invalidation
in the core. Folio release truncates memory; ->free_folio() calls
kvm_arch_gmem_reclaim() when the folio is removed from the page cache.
PFN release removes host mappings and calls kvm_arch_gmem_reclaim()
before put_pfn_range() releases the memory.
Use these operations for hole punching, provider revocation and final
inode cleanup.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/guest_memfd.h | 13 +++-
samples/kvm/mem_provider_sample.c | 3 +-
virt/kvm/guest_memfd.c | 106 +++++++++++++++++++++---------
3 files changed, 88 insertions(+), 34 deletions(-)
diff --git a/include/linux/guest_memfd.h b/include/linux/guest_memfd.h
index 06f094a2fd49..2ed929a9dca5 100644
--- a/include/linux/guest_memfd.h
+++ b/include/linux/guest_memfd.h
@@ -49,6 +49,12 @@ struct guest_memfd_handle {
* failure; it initially points to the range start. NULL
* selects folio validation for folio providers; for
* other providers, NULL skips this validation.
+ * @release_range: Remove host mappings and release memory in [start, end),
+ * after core guest invalidation, under the inode invalidate
+ * write lock and any provider lock. Also called at final
+ * inode cleanup, when no bindings remain. The optional pfn
+ * identifies memory revoked by the provider. release_backing
+ * is false for provider-initiated revocation, true otherwise.
* @alloc_folio: Allocate an uninserted folio for a given index and NUMA policy.
* Must return a pointer to a struct folio on success, or an
* ERR_PTR() error code on failure. Must never return NULL.
@@ -86,6 +92,9 @@ struct guest_memfd_provider_operations {
void (*unlock)(void *provider);
int (*prepare_attributes)(struct inode *inode, void *provider, pgoff_t start,
size_t nr_pages, u64 memory_attrs, pgoff_t *err_index);
+ int (*release_range)(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs, bool release_backing);
struct folio *(*alloc_folio)(void *provider, pgoff_t index,
struct mempolicy *mpol);
void (*invalidate_folio)(void *provider, struct folio *folio);
@@ -99,10 +108,10 @@ struct guest_memfd_provider_operations {
#ifdef CONFIG_KVM_GUEST_MEMFD
int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
- const unsigned long *pfn);
+ const unsigned long *pfn, u64 pfn_attrs);
#else
static inline int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
- const unsigned long *pfn)
+ const unsigned long *pfn, u64 pfn_attrs)
{
return -EOPNOTSUPP;
}
diff --git a/samples/kvm/mem_provider_sample.c b/samples/kvm/mem_provider_sample.c
index 8eca914c49ef..4b7a8f760fdf 100644
--- a/samples/kvm/mem_provider_sample.c
+++ b/samples/kvm/mem_provider_sample.c
@@ -88,7 +88,8 @@ static void mps_set_present(struct mps_info *info, unsigned long start,
if (info->gmem_inode) {
unsigned long pfn = info->base_pfn + start;
- kvm_gmem_revoke(info->gmem_inode, start, end - start, &pfn);
+ kvm_gmem_revoke(info->gmem_inode, start, end - start,
+ &pfn, 0);
mps_zero_range(info, start, end - start);
}
}
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index ffe0a8e0f2dd..a224ae969d79 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -56,6 +56,12 @@ static int gmem_folio_lookup_pfn_range(struct inode *inode, void *provider,
pgoff_t index, unsigned long *pfn, size_t *nr_pages, u64 *pfn_attrs);
static int gmem_folio_prepare_attributes(struct inode *inode, void *provider,
pgoff_t start, size_t nr_pages, u64 attrs, pgoff_t *err_index);
+static int gmem_folio_release_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs, bool release_backing);
+static int gmem_pfn_release_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs, bool release_backing);
static __always_inline struct gmem_inode *GMEM_I(struct inode *inode)
{
@@ -94,6 +100,7 @@ static const struct guest_memfd_provider_operations gmem_native_provider_ops = {
.get_pfn = gmem_folio_get_pfn,
.lookup_pfn_range = gmem_folio_lookup_pfn_range,
.prepare_attributes = gmem_folio_prepare_attributes,
+ .release_range = gmem_folio_release_range,
};
static bool gmem_provider_ops_valid(struct gmem_inode *gi,
@@ -141,6 +148,11 @@ static void gmem_provider_init_ops(struct gmem_inode *gi,
}
if (!ops->prepare_attributes)
gi->provider_operations.prepare_attributes = gmem_folio_prepare_attributes;
+ if (!ops->release_range)
+ gi->provider_operations.release_range = gmem_folio_release_range;
+ } else {
+ if (!ops->release_range)
+ gi->provider_operations.release_range = gmem_pfn_release_range;
}
gi->provider_ops = &gi->provider_operations;
}
@@ -582,19 +594,66 @@ static void kvm_gmem_pfn_reclaim(struct gmem_inode *gi, pgoff_t start, pgoff_t e
cond_resched();
}
}
-#else
-static void kvm_gmem_pfn_reclaim(struct gmem_inode *gi, pgoff_t start, pgoff_t end) {}
#endif
+/*
+ * Folios are managed by the page cache. Truncation removes them, and the MM
+ * invokes ->free_folio(), which calls kvm_arch_gmem_reclaim(). Keep that
+ * cleanup in ->free_folio() instead of duplicating it in the release path.
+ */
+static int gmem_folio_release_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs, bool release_backing)
+{
+ loff_t offset = (loff_t)start << PAGE_SHIFT;
+ loff_t len = (loff_t)(end - start) << PAGE_SHIFT;
+
+ truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1);
+ return 0;
+}
+
+static void gmem_provider_reclaim(struct gmem_inode *gi, pgoff_t start,
+ pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs)
+{
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+ if (pfn_attrs & GUEST_MEMFD_PFN_MMIO)
+ return;
+ if (pfn)
+ kvm_arch_gmem_reclaim(*pfn, end - start);
+ else
+ kvm_gmem_pfn_reclaim(gi, start, end);
+#endif
+}
+
+static int gmem_pfn_release_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end, const unsigned long *pfn,
+ u64 pfn_attrs, bool release_backing)
+{
+ struct gmem_inode *gi = GMEM_I(inode);
+
+ unmap_mapping_pages(inode->i_mapping, start, end - start, true);
+ gmem_provider_reclaim(gi, start, end, pfn, pfn_attrs);
+
+ if (release_backing)
+ gmem_provider_put_pfn_range(gi, start, end - start);
+
+ return 0;
+}
+
static int __kvm_gmem_revoke(struct inode *inode, pgoff_t start,
size_t nr_pages, const unsigned long *pfn,
- bool put_pfn_range)
+ u64 pfn_attrs, bool release_backing)
{
enum kvm_gfn_range_filter filter = kvm_gmem_get_all_gfns_filter(inode);
pgoff_t max_pages = i_size_read(inode) >> PAGE_SHIFT;
struct gmem_inode *gi = GMEM_I(inode);
- loff_t offset, len;
+ const struct guest_memfd_provider_operations *ops = gi->provider_ops;
pgoff_t end;
+ int r;
+
+ if (!ops->release_range)
+ return -EOPNOTSUPP;
if (start >= max_pages)
return 0;
@@ -603,8 +662,6 @@ static int __kvm_gmem_revoke(struct inode *inode, pgoff_t start,
if (!nr_pages)
return 0;
- offset = (loff_t)start << PAGE_SHIFT;
- len = (loff_t)nr_pages << PAGE_SHIFT;
end = start + nr_pages;
/*
@@ -617,32 +674,20 @@ static int __kvm_gmem_revoke(struct inode *inode, pgoff_t start,
kvm_gmem_invalidate_start(inode, start, end, filter);
- if (kvm_gmem_is_pfn_mode(GMEM_I(inode))) {
- unmap_mapping_range(inode->i_mapping, offset, len, 0);
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
- if (pfn)
- kvm_arch_gmem_reclaim(*pfn, nr_pages);
- else
- kvm_gmem_pfn_reclaim(GMEM_I(inode), start, end);
-#endif
- if (put_pfn_range)
- gmem_provider_put_pfn_range(GMEM_I(inode), start, nr_pages);
- } else {
- truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1);
- }
+ r = ops->release_range(inode, gi->provider, start, end, pfn, pfn_attrs,
+ release_backing);
kvm_gmem_invalidate_end(inode, start, end);
gmem_provider_unlock_updates(gi);
-
filemap_invalidate_unlock(inode->i_mapping);
- return 0;
+ return r;
}
static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
{
return __kvm_gmem_revoke(inode, offset >> PAGE_SHIFT, len >> PAGE_SHIFT,
- NULL, true);
+ NULL, 0, true);
}
/**
@@ -653,6 +698,7 @@ static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
* @pfn: The physical address / frame number being revoked. Directly
* reclaims this physical range without querying the provider's
* .lookup_pfn_range().
+ * @pfn_attrs: GUEST_MEMFD_PFN_* attributes of the supplied PFNs.
*
* guest_memfd will do everything to make sure the range revoked is no longer in
* use across guest and host mappings, as well as reclaim any confidential
@@ -665,9 +711,9 @@ static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
* Return: 0 on success, or a negative error code on failure.
*/
int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
- const unsigned long *pfn)
+ const unsigned long *pfn, u64 pfn_attrs)
{
- return __kvm_gmem_revoke(inode, start, nr_pages, pfn, false);
+ return __kvm_gmem_revoke(inode, start, nr_pages, pfn, pfn_attrs, false);
}
EXPORT_SYMBOL_GPL(kvm_gmem_revoke);
@@ -1887,17 +1933,15 @@ static void kvm_gmem_destroy_inode(struct inode *inode)
{
struct gmem_inode *gi = GMEM_I(inode);
- /*
- * Skip if the provider was not yet set up, as iput() can reach this on
- * error paths before provider_ops is initialized.
- */
- if (gi->provider_ops && kvm_gmem_is_pfn_mode(gi)) {
+ /* Provider setup may have failed before this inode was published. */
+ if (gi->provider_ops) {
pgoff_t nr_pages = i_size_read(inode) >> PAGE_SHIFT;
filemap_invalidate_lock(inode->i_mapping);
gmem_provider_lock_updates(gi);
- kvm_gmem_pfn_reclaim(gi, 0, nr_pages);
- gmem_provider_put_pfn_range(gi, 0, nr_pages);
+ if (nr_pages)
+ gi->provider_ops->release_range(inode, gi->provider, 0,
+ nr_pages, NULL, 0, true);
gmem_provider_unlock_updates(gi);
filemap_invalidate_unlock(inode->i_mapping);
}
--
2.43.0
next prev parent reply other threads:[~2026-11-12 7:42 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 7:41 [RFC PATCH v5 00/16] KVM: guest_memfd: Add a common framework for device memory providers Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 01/16] KVM: guest_memfd: Add a handle for provider PFNs Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 02/16] KVM: guest_memfd: Get PFNs through common provider operations Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 03/16] KVM: guest_memfd: Let providers coordinate mapping changes Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 04/16] KVM: guest_memfd: Add provider PFN range lookup Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 05/16] KVM: guest_memfd: Let providers validate attribute changes Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 06/16] KVM: guest_memfd: Share memory through provider range lookup Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` Aneesh Kumar K.V (Arm) [this message]
2026-11-12 7:41 ` [RFC PATCH v5 08/16] KVM: guest_memfd: Use provider operations for fallocate allocation Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 09/16] KVM: guest_memfd: Use provider operations for host mappings and faults Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 10/16] KVM: guest_memfd: Let providers prepare memory regions Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 11/16] KVM: guest_memfd: Pass KVM explicitly to provider attach Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 12/16] KVM: guest_memfd: Support devices through common provider operations Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 13/16] KVM: guest_memfd: Notify providers after private attribute updates Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 14/16] KVM: guest_memfd: Enable shared host mappings for device providers Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 15/16] KVM: guest_memfd: Notify providers during file release Aneesh Kumar K.V (Arm)
2026-11-12 7:41 ` [RFC PATCH v5 16/16] KVM: guest_memfd: Invalidate mappings before memslot unbind Aneesh Kumar K.V (Arm)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112074120.1570072-8-aneesh.kumar@kernel.org \
--to=aneesh.kumar@kernel.org \
--cc=ackerleytng@google.com \
--cc=aik@amd.com \
--cc=catalin.marinas@arm.com \
--cc=david@kernel.org \
--cc=dwmw2@infradead.org \
--cc=griffoul@gmail.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=kevin.tian@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=patches@lists.linux.dev \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
--cc=steven.price@arm.com \
--cc=suzuki.poulose@arm.com \
--cc=yilun.xu@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.