From: "Mike Rapoport (Microsoft)" <[email protected]>

* Export handle_userfault() for KVM module so that fault() handler in
  guest_memfd would be able to notify userspace about page faults in its
  address space.
* Implement get_pagecache_folio() for guest_memfd.
* And finally, introduce UFFD_FEATURE_MINOR_GENERIC that will allow
  using userfaultfd minor mode with memory types other than shmem and
  hugetlb provided they are allowed to call handle_userfault() and
  implement get_pagecache_folio().

Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
---
 fs/userfaultfd.c                 |  4 +++-
 include/uapi/linux/userfaultfd.h |  8 +++++++-
 virt/kvm/guest_memfd.c           | 30 ++++++++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 54c6cc7fe9c6..964fa2662d5c 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -537,6 +537,7 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned 
long reason)
 out:
        return ret;
 }
+EXPORT_SYMBOL_FOR_MODULES(handle_userfault, "kvm");
 
 static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
                                              struct userfaultfd_wait_queue 
*ewq)
@@ -1978,7 +1979,8 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,
        uffdio_api.features = UFFD_API_FEATURES;
 #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
        uffdio_api.features &=
-               ~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM);
+               ~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM |
+                 UFFD_FEATURE_MINOR_GENERIC);
 #endif
 #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP
        uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP;
diff --git a/include/uapi/linux/userfaultfd.h b/include/uapi/linux/userfaultfd.h
index 2841e4ea8f2c..c5cbd4a5a26e 100644
--- a/include/uapi/linux/userfaultfd.h
+++ b/include/uapi/linux/userfaultfd.h
@@ -42,7 +42,8 @@
                           UFFD_FEATURE_WP_UNPOPULATED |        \
                           UFFD_FEATURE_POISON |                \
                           UFFD_FEATURE_WP_ASYNC |              \
-                          UFFD_FEATURE_MOVE)
+                          UFFD_FEATURE_MOVE |                  \
+                          UFFD_FEATURE_MINOR_GENERIC)
 #define UFFD_API_IOCTLS                                \
        ((__u64)1 << _UFFDIO_REGISTER |         \
         (__u64)1 << _UFFDIO_UNREGISTER |       \
@@ -210,6 +211,10 @@ struct uffdio_api {
         * UFFD_FEATURE_MINOR_SHMEM indicates the same support as
         * UFFD_FEATURE_MINOR_HUGETLBFS, but for shmem-backed pages instead.
         *
+        * UFFD_FEATURE_MINOR_GENERIC indicates that minor faults can be
+        * intercepted for file-backed memory in case subsystem backing this
+        * memory supports it.
+        *
         * UFFD_FEATURE_EXACT_ADDRESS indicates that the exact address of page
         * faults would be provided and the offset within the page would not be
         * masked.
@@ -248,6 +253,7 @@ struct uffdio_api {
 #define UFFD_FEATURE_POISON                    (1<<14)
 #define UFFD_FEATURE_WP_ASYNC                  (1<<15)
 #define UFFD_FEATURE_MOVE                      (1<<16)
+#define UFFD_FEATURE_MINOR_GENERIC             (1<<17)
        __u64 features;
 
        __u64 ioctls;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index fbca8c0972da..5e3c63307fdf 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -4,6 +4,7 @@
 #include <linux/kvm_host.h>
 #include <linux/pagemap.h>
 #include <linux/anon_inodes.h>
+#include <linux/userfaultfd_k.h>
 
 #include "kvm_mm.h"
 
@@ -369,6 +370,12 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct 
vm_fault *vmf)
                return vmf_error(err);
        }
 
+       if (userfaultfd_minor(vmf->vma)) {
+               folio_unlock(folio);
+               folio_put(folio);
+               return handle_userfault(vmf, VM_UFFD_MINOR);
+       }
+
        if (WARN_ON_ONCE(folio_test_large(folio))) {
                ret = VM_FAULT_SIGBUS;
                goto out_folio;
@@ -390,8 +397,31 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct 
vm_fault *vmf)
        return ret;
 }
 
+#ifdef CONFIG_USERFAULTFD
+static struct folio *kvm_gmem_get_pagecache_folio(struct vm_area_struct *vma,
+                                                 pgoff_t pgoff)
+{
+       struct inode *inode = file_inode(vma->vm_file);
+       struct folio *folio;
+
+       folio = kvm_gmem_get_folio(inode, pgoff);
+       if (IS_ERR_OR_NULL(folio))
+               return folio;
+
+       if (!folio_test_uptodate(folio)) {
+               clear_highpage(folio_page(folio, 0));
+               kvm_gmem_mark_prepared(folio);
+       }
+
+       return folio;
+}
+#endif
+
 static const struct vm_operations_struct kvm_gmem_vm_ops = {
        .fault = kvm_gmem_fault_user_mapping,
+#ifdef CONFIG_USERFAULTFD
+       .get_pagecache_folio    = kvm_gmem_get_pagecache_folio,
+#endif
 };
 
 static int kvm_gmem_mmap(struct file *file, struct vm_area_struct *vma)
-- 
2.50.1


Reply via email to