Implement kvm_gmem_migrate_folio() using filemap_migrate_folio() for
non-confidential VMs but don't enable it yet. guest_memfd mappings
are still marked unmovable, so MM never calls it.

Migration invokes ->migrate_folio() callback with  folios already
locked and acquires the invalidate lock afterwards. While
kvm_gmem_punch_hole() acquires the invalidate lock and takes folio
lock afterwards during truncation. This could invert the lock order
and cause an ABBA-deadlock situation. Use
filemap_invalidate_trylock_shared() to avoid this.

A subsequent patch adds an opt-in flag to change the mapping to
movable and enable migration. Non-confidential VMs can use
host-side copy via folio_mc_copy(), whereas confidential VMs will
require firmware-assisted copying before they can opt in.

Signed-off-by: Shivank Garg <[email protected]>
---
 virt/kvm/guest_memfd.c | 36 ++++++++++++++++++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index d3b1186b03b3..ec881cabe8e3 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -488,13 +488,45 @@ static struct file_operations kvm_gmem_fops = {
        .fallocate      = kvm_gmem_fallocate,
 };
 
+#ifdef CONFIG_MIGRATION
 static int kvm_gmem_migrate_folio(struct address_space *mapping,
                                  struct folio *dst, struct folio *src,
                                  enum migrate_mode mode)
 {
-       WARN_ON_ONCE(1);
-       return -EINVAL;
+       struct inode *inode = mapping->host;
+       pgoff_t start, end;
+       int ret;
+
+       /*
+        * Migration invokes ->migrate_folio() while holding the folio lock.
+        * Use a non-blocking trylock to avoid inverting the lock order with
+        * truncation, which takes the invalidate lock before locking the
+        * folios.
+        */
+       if (!filemap_invalidate_trylock_shared(mapping))
+               return -EAGAIN;
+
+       start = src->index;
+       end = start + folio_nr_pages(src);
+
+       kvm_gmem_invalidate_start(inode, start, end);
+
+       /*
+        * For non-confidential guests the folio is host-readable, so
+        * filemap_migrate_folio() can copy the contents itself via
+        * folio_mc_copy().
+        * For confidential guests, this would need firmware assistance.
+        */
+       ret = filemap_migrate_folio(mapping, dst, src, mode);
+
+       kvm_gmem_invalidate_end(inode, start, end);
+
+       filemap_invalidate_unlock_shared(mapping);
+       return ret;
 }
+#else
+#define kvm_gmem_migrate_folio NULL
+#endif
 
 static int kvm_gmem_error_folio(struct address_space *mapping, struct folio 
*folio)
 {

-- 
2.43.0


Reply via email to