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 08/16] KVM: guest_memfd: Use provider operations for fallocate allocation
Date: Thu, 12 Nov 2026 13:11:12 +0530 [thread overview]
Message-ID: <20261112074120.1570072-9-aneesh.kumar@kernel.org> (raw)
In-Reply-To: <20261112074120.1570072-1-aneesh.kumar@kernel.org>
Use allocate_range() for fallocate allocation so providers can supply
allocation and release callback. When a provider supplies neither callback,
guest_memfd handles allocation and release for it. Providers that supply
only release_range() cannot use fallocate(), including hole punching.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
include/linux/guest_memfd.h | 8 +++
virt/kvm/guest_memfd.c | 122 +++++++++++++++++++++++-------------
2 files changed, 87 insertions(+), 43 deletions(-)
diff --git a/include/linux/guest_memfd.h b/include/linux/guest_memfd.h
index 2ed929a9dca5..e11b2392b001 100644
--- a/include/linux/guest_memfd.h
+++ b/include/linux/guest_memfd.h
@@ -49,12 +49,18 @@ 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.
+ * @allocate_range: Preallocate [start, end), without architecture private
+ * preparation. Called with the inode invalidate lock held
+ * for read.
* @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.
+ * Supplying this without allocate_range()
+ * supports a fixed memory range and disables fallocate(),
+ * including hole punching.
* @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.
@@ -92,6 +98,8 @@ 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 (*allocate_range)(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end);
int (*release_range)(struct inode *inode, void *provider,
pgoff_t start, pgoff_t end, const unsigned long *pfn,
u64 pfn_attrs, bool release_backing);
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index a224ae969d79..c6b1814b5e7e 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -56,6 +56,10 @@ 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_allocate_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end);
+static int gmem_pfn_allocate_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end);
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);
@@ -100,6 +104,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,
+ .allocate_range = gmem_folio_allocate_range,
.release_range = gmem_folio_release_range,
};
@@ -122,6 +127,10 @@ static bool gmem_provider_ops_valid(struct gmem_inode *gi,
if (ops->invalidate_folio && !ops->alloc_folio)
return false;
+ /* A fixed memory range may support release without supporting allocation. */
+ if (ops->allocate_range && !ops->release_range)
+ return false;
+
/* Custom folio get_pfn() needs matching host mapping callbacks. */
if (ops->alloc_folio && ops->get_pfn &&
(gi->flags & GUEST_MEMFD_FLAG_MMAP))
@@ -148,11 +157,15 @@ 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)
+ if (!ops->release_range) {
+ gi->provider_operations.allocate_range = gmem_folio_allocate_range;
gi->provider_operations.release_range = gmem_folio_release_range;
+ }
} else {
- if (!ops->release_range)
+ if (!ops->release_range) {
+ gi->provider_operations.allocate_range = gmem_pfn_allocate_range;
gi->provider_operations.release_range = gmem_pfn_release_range;
+ }
}
gi->provider_ops = &gi->provider_operations;
}
@@ -717,47 +730,11 @@ int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
}
EXPORT_SYMBOL_GPL(kvm_gmem_revoke);
-static long kvm_gmem_allocate(struct inode *inode, loff_t offset, loff_t len)
+static int gmem_folio_allocate_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end)
{
- struct address_space *mapping = inode->i_mapping;
- struct gmem_inode *gi = GMEM_I(inode);
- pgoff_t start, index, end;
- int r;
-
- /* Dedicated guest is immutable by default. */
- if (offset + len > i_size_read(inode))
- return -EINVAL;
-
- filemap_invalidate_lock_shared(mapping);
-
- start = offset >> PAGE_SHIFT;
- end = (offset + len) >> PAGE_SHIFT;
-
- r = 0;
- if (kvm_gmem_is_pfn_mode(gi)) {
- for (index = start; index < end; ) {
- pgoff_t next_boundary;
- pgoff_t max_nr_pages;
- struct guest_memfd_handle handle;
-
- if (signal_pending(current)) {
- r = -EINTR;
- break;
- }
-
- r = gmem_provider_get_pfn(gi, index, &handle);
- if (r)
- break;
-
- max_nr_pages = 1UL << handle.max_order;
- gmem_unlock_handle(&handle);
- gmem_put_handle(&handle);
- next_boundary = ALIGN_DOWN(index + max_nr_pages, max_nr_pages);
- index += min_t(size_t, next_boundary - index, end - index);
- cond_resched();
- }
- goto out_unlock;
- }
+ pgoff_t index;
+ int r = 0;
for (index = start; index < end; ) {
struct folio *folio;
@@ -785,7 +762,62 @@ static long kvm_gmem_allocate(struct inode *inode, loff_t offset, loff_t len)
cond_resched();
}
-out_unlock:
+ return r;
+}
+
+static int gmem_pfn_allocate_range(struct inode *inode, void *provider,
+ pgoff_t start, pgoff_t end)
+{
+ struct gmem_inode *gi = GMEM_I(inode);
+ pgoff_t index;
+ int r = 0;
+
+ for (index = start; index < end; ) {
+ pgoff_t next_boundary;
+ pgoff_t max_nr_pages;
+ struct guest_memfd_handle handle;
+
+ if (signal_pending(current)) {
+ r = -EINTR;
+ break;
+ }
+
+ r = gmem_provider_get_pfn(gi, index, &handle);
+ if (r)
+ break;
+
+ max_nr_pages = 1UL << handle.max_order;
+ gmem_unlock_handle(&handle);
+ gmem_put_handle(&handle);
+ next_boundary = ALIGN_DOWN(index + max_nr_pages, max_nr_pages);
+ index += min_t(size_t, next_boundary - index, end - index);
+ cond_resched();
+ }
+
+ return r;
+}
+
+static long kvm_gmem_allocate(struct inode *inode, loff_t offset, loff_t len)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct gmem_inode *gi = GMEM_I(inode);
+ const struct guest_memfd_provider_operations *ops = gi->provider_ops;
+ pgoff_t start, end;
+ int r;
+
+ if (!ops->allocate_range)
+ return -EOPNOTSUPP;
+
+ /* Dedicated guest is immutable by default. */
+ if (offset + len > i_size_read(inode))
+ return -EINVAL;
+
+ filemap_invalidate_lock_shared(mapping);
+
+ start = offset >> PAGE_SHIFT;
+ end = (offset + len) >> PAGE_SHIFT;
+ r = ops->allocate_range(inode, gi->provider, start, end);
+
filemap_invalidate_unlock_shared(mapping);
return r;
@@ -796,6 +828,10 @@ static long kvm_gmem_fallocate(struct file *file, int mode, loff_t offset,
{
int ret;
+ /* Providers without allocation support cannot be hole punched either. */
+ if (!GMEM_I(file_inode(file))->provider_ops->allocate_range)
+ return -EOPNOTSUPP;
+
if (!(mode & FALLOC_FL_KEEP_SIZE))
return -EOPNOTSUPP;
--
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 ` [RFC PATCH v5 07/16] KVM: guest_memfd: Release and revoke memory through provider operations 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 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-9-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.