All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals
@ 2022-06-17  1:41 Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE Peter Xu
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Peter Xu @ 2022-06-17  1:41 UTC (permalink / raw)
  To: kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton, peterx

[Marked as RFC for now]

One issue was reported that libvirt won't be able to stop the virtual
machine using QMP command "stop" during a paused postcopy migration [1].

It won't work because "stop the VM" operation requires the hypervisor to
kick all the vcpu threads out using SIG_IPI in QEMU (which is translated to
a SIGUSR1).  However since during a paused postcopy, the vcpu threads are
hang death at handle_userfault() so there're simply not responding to the
kicks.  Further, the "stop" command will further hang the QMP channel.

The mm has facility to process generic signal (FAULT_FLAG_INTERRUPTIBLE),
however it's only used in the PF handlers only, not in GUP. Unluckily, KVM
is a heavy GUP user on guest page faults.  It means we won't be able to
interrupt a long page fault for KVM fetching guest pages with what we have
right now.

I think it's reasonable for GUP to only listen to fatal signals, as most of
the GUP users are not really ready to handle such case.  But actually KVM
is not such an user, and KVM actually has rich infrastructure to handle
even generic signals, and properly deliver the signal to the userspace.
Then the page fault can be retried in the next KVM_RUN.

This patchset added FOLL_INTERRUPTIBLE to enable FAULT_FLAG_INTERRUPTIBLE,
and let KVM be the first one to use it.

Tests
=====

I created a postcopy environment, pause the migration by shutting down the
network to emulate a network failure (so the handle_userfault() will stuck
for a long time), then I tried three things:

  (1) Sending QMP command "stop" to QEMU monitor,
  (2) Hitting Ctrl-C from QEMU cmdline,
  (3) GDB attach to the dest QEMU process.

Before this patchset, all three use case hang.  After the patchset, all
work just like when there's not network failure at all.

Please have a look, thanks.

[1] https://gitlab.com/qemu-project/qemu/-/issues/1052

Peter Xu (4):
  mm/gup: Add FOLL_INTERRUPTIBLE
  kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot()
  kvm: Add new pfn error KVM_PFN_ERR_INTR
  kvm/x86: Allow to respond to generic signals during slow page faults

 arch/arm64/kvm/mmu.c                   |  5 ++--
 arch/powerpc/kvm/book3s_64_mmu_hv.c    |  5 ++--
 arch/powerpc/kvm/book3s_64_mmu_radix.c |  5 ++--
 arch/x86/kvm/mmu/mmu.c                 | 19 ++++++++----
 include/linux/kvm_host.h               | 21 ++++++++++++-
 include/linux/mm.h                     |  1 +
 mm/gup.c                               | 33 ++++++++++++++++++---
 virt/kvm/kvm_main.c                    | 41 ++++++++++++++++----------
 virt/kvm/kvm_mm.h                      |  6 ++--
 virt/kvm/pfncache.c                    |  2 +-
 10 files changed, 104 insertions(+), 34 deletions(-)

-- 
2.32.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE
  2022-06-17  1:41 [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals Peter Xu
@ 2022-06-17  1:41 ` Peter Xu
  2022-06-21  8:23   ` David Hildenbrand
  2022-06-17  1:41 ` [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot() Peter Xu
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Peter Xu @ 2022-06-17  1:41 UTC (permalink / raw)
  To: kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton, peterx

We have had FAULT_FLAG_INTERRUPTIBLE but it was never applied to GUPs.  One
issue with it is that not all GUP paths are able to handle signal delivers
besides SIGKILL.

That's not ideal for the GUP users who are actually able to handle these
cases, like KVM.

KVM uses GUP extensively on faulting guest pages, during which we've got
existing infrastructures to retry a page fault at a later time.  Allowing
the GUP to be interrupted by generic signals can make KVM related threads
to be more responsive.  For examples:

  (1) SIGUSR1: which QEMU/KVM uses to deliver an inter-process IPI,
      e.g. when the admin issues a vm_stop QMP command, SIGUSR1 can be
      generated to kick the vcpus out of kernel context immediately,

  (2) SIGINT: which can be used with interactive hypervisor users to stop a
      virtual machine with Ctrl-C without any delays/hangs,

  (3) SIGTRAP: which grants GDB capability even during page faults that are
      stuck for a long time.

Normally hypervisor will be able to receive these signals properly, but not
if we're stuck in a GUP for a long time for whatever reason.  It happens
easily with a stucked postcopy migration when e.g. a network temp failure
happens, then some vcpu threads can hang death waiting for the pages.  With
the new FOLL_INTERRUPTIBLE, we can allow GUP users like KVM to selectively
enable the ability to trap these signals.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/linux/mm.h |  1 +
 mm/gup.c           | 33 +++++++++++++++++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index bc8f326be0ce..ebdf8a6b86c1 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2941,6 +2941,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
 #define FOLL_SPLIT_PMD	0x20000	/* split huge pmd before returning */
 #define FOLL_PIN	0x40000	/* pages must be released via unpin_user_page */
 #define FOLL_FAST_ONLY	0x80000	/* gup_fast: prevent fall-back to slow gup */
+#define FOLL_INTERRUPTIBLE  0x100000 /* allow interrupts from generic signals */
 
 /*
  * FOLL_PIN and FOLL_LONGTERM may be used in various combinations with each
diff --git a/mm/gup.c b/mm/gup.c
index 551264407624..ad74b137d363 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -933,8 +933,17 @@ static int faultin_page(struct vm_area_struct *vma,
 		fault_flags |= FAULT_FLAG_WRITE;
 	if (*flags & FOLL_REMOTE)
 		fault_flags |= FAULT_FLAG_REMOTE;
-	if (locked)
+	if (locked) {
 		fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+		/*
+		 * We should only grant FAULT_FLAG_INTERRUPTIBLE when we're
+		 * (at least) killable.  It also mostly means we're not
+		 * with NOWAIT.  Otherwise ignore FOLL_INTERRUPTIBLE since
+		 * it won't make a lot of sense to be used alone.
+		 */
+		if (*flags & FOLL_INTERRUPTIBLE)
+			fault_flags |= FAULT_FLAG_INTERRUPTIBLE;
+	}
 	if (*flags & FOLL_NOWAIT)
 		fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT;
 	if (*flags & FOLL_TRIED) {
@@ -1322,6 +1331,22 @@ int fixup_user_fault(struct mm_struct *mm,
 }
 EXPORT_SYMBOL_GPL(fixup_user_fault);
 
+/*
+ * GUP always responds to fatal signals.  When FOLL_INTERRUPTIBLE is
+ * specified, it'll also respond to generic signals.  The caller of GUP
+ * that has FOLL_INTERRUPTIBLE should take care of the GUP interruption.
+ */
+static bool gup_signal_pending(unsigned int flags)
+{
+	if (fatal_signal_pending(current))
+		return true;
+
+	if (!(flags & FOLL_INTERRUPTIBLE))
+		return false;
+
+	return signal_pending(current);
+}
+
 /*
  * Please note that this function, unlike __get_user_pages will not
  * return 0 for nr_pages > 0 without FOLL_NOWAIT
@@ -1403,11 +1428,11 @@ static __always_inline long __get_user_pages_locked(struct mm_struct *mm,
 		 * Repeat on the address that fired VM_FAULT_RETRY
 		 * with both FAULT_FLAG_ALLOW_RETRY and
 		 * FAULT_FLAG_TRIED.  Note that GUP can be interrupted
-		 * by fatal signals, so we need to check it before we
+		 * by fatal signals of even common signals, depending on
+		 * the caller's request. So we need to check it before we
 		 * start trying again otherwise it can loop forever.
 		 */
-
-		if (fatal_signal_pending(current)) {
+		if (gup_signal_pending(flags)) {
 			if (!pages_done)
 				pages_done = -EINTR;
 			break;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot()
  2022-06-17  1:41 [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE Peter Xu
@ 2022-06-17  1:41 ` Peter Xu
  2022-06-17 21:53   ` kernel test robot
  2022-06-17  1:41 ` [PATCH RFC 3/4] kvm: Add new pfn error KVM_PFN_ERR_INTR Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 4/4] kvm/x86: Allow to respond to generic signals during slow page faults Peter Xu
  3 siblings, 1 reply; 8+ messages in thread
From: Peter Xu @ 2022-06-17  1:41 UTC (permalink / raw)
  To: kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton, peterx

Merge two boolean parameters into a bitmask flag called kvm_gtp_flag_t for
__gfn_to_pfn_memslot().  This cleans the parameter lists, and also prepare
for new boolean to be added to __gfn_to_pfn_memslot().

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 arch/arm64/kvm/mmu.c                   |  5 ++--
 arch/powerpc/kvm/book3s_64_mmu_hv.c    |  5 ++--
 arch/powerpc/kvm/book3s_64_mmu_radix.c |  5 ++--
 arch/x86/kvm/mmu/mmu.c                 | 10 +++----
 include/linux/kvm_host.h               |  9 ++++++-
 virt/kvm/kvm_main.c                    | 37 +++++++++++++++-----------
 virt/kvm/kvm_mm.h                      |  6 +++--
 virt/kvm/pfncache.c                    |  2 +-
 8 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index f5651a05b6a8..ce8066ded15b 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1204,8 +1204,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 	 */
 	smp_rmb();
 
-	pfn = __gfn_to_pfn_memslot(memslot, gfn, false, NULL,
-				   write_fault, &writable, NULL);
+	pfn = __gfn_to_pfn_memslot(memslot, gfn,
+				   write_fault ? KVM_GTP_WRITE : 0,
+				   false, NULL, &writable, NULL);
 	if (pfn == KVM_PFN_ERR_HWPOISON) {
 		kvm_send_hwpoison_signal(hva, vma_shift);
 		return 0;
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 514fd45c1994..2f5fad2e1b7f 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -598,8 +598,9 @@ int kvmppc_book3s_hv_page_fault(struct kvm_vcpu *vcpu,
 		write_ok = true;
 	} else {
 		/* Call KVM generic code to do the slow-path check */
-		pfn = __gfn_to_pfn_memslot(memslot, gfn, false, NULL,
-					   writing, &write_ok, NULL);
+		pfn = __gfn_to_pfn_memslot(memslot, gfn,
+					   writing ? KVM_GTP_WRITE : 0,
+					   false, NULL, &write_ok, NULL);
 		if (is_error_noslot_pfn(pfn))
 			return -EFAULT;
 		page = NULL;
diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c
index 42851c32ff3b..232b17c75b83 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_radix.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c
@@ -845,8 +845,9 @@ int kvmppc_book3s_instantiate_page(struct kvm_vcpu *vcpu,
 		unsigned long pfn;
 
 		/* Call KVM generic code to do the slow-path check */
-		pfn = __gfn_to_pfn_memslot(memslot, gfn, false, NULL,
-					   writing, upgrade_p, NULL);
+		pfn = __gfn_to_pfn_memslot(memslot, gfn,
+					   writing ? KVM_GTP_WRITE : 0,
+					   NULL, upgrade_p, NULL);
 		if (is_error_noslot_pfn(pfn))
 			return -EFAULT;
 		page = NULL;
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index f4653688fa6d..e92f1ab63d6a 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -3968,6 +3968,7 @@ void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu, struct kvm_async_pf *work)
 
 static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
 {
+	kvm_gtp_flag_t flags = fault->write ? KVM_GTP_WRITE : 0;
 	struct kvm_memory_slot *slot = fault->slot;
 	bool async;
 
@@ -3999,8 +4000,8 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
 	}
 
 	async = false;
-	fault->pfn = __gfn_to_pfn_memslot(slot, fault->gfn, false, &async,
-					  fault->write, &fault->map_writable,
+	fault->pfn = __gfn_to_pfn_memslot(slot, fault->gfn, flags,
+					  &async, &fault->map_writable,
 					  &fault->hva);
 	if (!async)
 		return RET_PF_CONTINUE; /* *pfn has correct page already */
@@ -4016,9 +4017,8 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
 		}
 	}
 
-	fault->pfn = __gfn_to_pfn_memslot(slot, fault->gfn, false, NULL,
-					  fault->write, &fault->map_writable,
-					  &fault->hva);
+	fault->pfn = __gfn_to_pfn_memslot(slot, fault->gfn, flags, NULL,
+					  &fault->map_writable, &fault->hva);
 	return RET_PF_CONTINUE;
 }
 
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index c20f2d55840c..b646b6fcaec6 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1146,8 +1146,15 @@ kvm_pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
 		      bool *writable);
 kvm_pfn_t gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn);
 kvm_pfn_t gfn_to_pfn_memslot_atomic(const struct kvm_memory_slot *slot, gfn_t gfn);
+
+/* gfn_to_pfn (gtp) flags */
+typedef unsigned int __bitwise kvm_gtp_flag_t;
+
+#define  KVM_GTP_WRITE          ((__force kvm_gtp_flag_t) BIT(0))
+#define  KVM_GTP_ATOMIC         ((__force kvm_gtp_flag_t) BIT(1))
+
 kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn,
-			       bool atomic, bool *async, bool write_fault,
+			       kvm_gtp_flag_t gtp_flags, bool *async,
 			       bool *writable, hva_t *hva);
 
 void kvm_release_pfn_clean(kvm_pfn_t pfn);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 64ec2222a196..952400b42ee9 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2444,9 +2444,11 @@ static bool hva_to_pfn_fast(unsigned long addr, bool write_fault,
  * The slow path to get the pfn of the specified host virtual address,
  * 1 indicates success, -errno is returned if error is detected.
  */
-static int hva_to_pfn_slow(unsigned long addr, bool *async, bool write_fault,
+static int hva_to_pfn_slow(unsigned long addr, bool *async,
+			   kvm_gtp_flag_t gtp_flags,
 			   bool *writable, kvm_pfn_t *pfn)
 {
+	bool write_fault = gtp_flags & KVM_GTP_WRITE;
 	unsigned int flags = FOLL_HWPOISON;
 	struct page *page;
 	int npages = 0;
@@ -2565,20 +2567,22 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
 /*
  * Pin guest page in memory and return its pfn.
  * @addr: host virtual address which maps memory to the guest
- * @atomic: whether this function can sleep
+ * @gtp_flags: kvm_gtp_flag_t flags (atomic, write, ..)
  * @async: whether this function need to wait IO complete if the
  *         host page is not in the memory
- * @write_fault: whether we should get a writable host page
  * @writable: whether it allows to map a writable host page for !@write_fault
  *
- * The function will map a writable host page for these two cases:
+ * The function will map a writable (KVM_GTP_WRITE set) host page for these
+ * two cases:
  * 1): @write_fault = true
  * 2): @write_fault = false && @writable, @writable will tell the caller
  *     whether the mapping is writable.
  */
-kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
-		     bool write_fault, bool *writable)
+kvm_pfn_t hva_to_pfn(unsigned long addr, kvm_gtp_flag_t gtp_flags, bool *async,
+		     bool *writable)
 {
+	bool write_fault = gtp_flags & KVM_GTP_WRITE;
+	bool atomic = gtp_flags & KVM_GTP_ATOMIC;
 	struct vm_area_struct *vma;
 	kvm_pfn_t pfn = 0;
 	int npages, r;
@@ -2592,7 +2596,7 @@ kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
 	if (atomic)
 		return KVM_PFN_ERR_FAULT;
 
-	npages = hva_to_pfn_slow(addr, async, write_fault, writable, &pfn);
+	npages = hva_to_pfn_slow(addr, async, gtp_flags, writable, &pfn);
 	if (npages == 1)
 		return pfn;
 
@@ -2625,10 +2629,11 @@ kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
 }
 
 kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn,
-			       bool atomic, bool *async, bool write_fault,
+			       kvm_gtp_flag_t gtp_flags, bool *async,
 			       bool *writable, hva_t *hva)
 {
-	unsigned long addr = __gfn_to_hva_many(slot, gfn, NULL, write_fault);
+	unsigned long addr = __gfn_to_hva_many(slot, gfn, NULL,
+					       gtp_flags & KVM_GTP_WRITE);
 
 	if (hva)
 		*hva = addr;
@@ -2651,28 +2656,30 @@ kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn,
 		writable = NULL;
 	}
 
-	return hva_to_pfn(addr, atomic, async, write_fault,
-			  writable);
+	return hva_to_pfn(addr, gtp_flags, async, writable);
 }
 EXPORT_SYMBOL_GPL(__gfn_to_pfn_memslot);
 
 kvm_pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
 		      bool *writable)
 {
-	return __gfn_to_pfn_memslot(gfn_to_memslot(kvm, gfn), gfn, false, NULL,
-				    write_fault, writable, NULL);
+	return __gfn_to_pfn_memslot(gfn_to_memslot(kvm, gfn), gfn,
+				    write_fault ? KVM_GTP_WRITE : 0,
+				    NULL, writable, NULL);
 }
 EXPORT_SYMBOL_GPL(gfn_to_pfn_prot);
 
 kvm_pfn_t gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn)
 {
-	return __gfn_to_pfn_memslot(slot, gfn, false, NULL, true, NULL, NULL);
+	return __gfn_to_pfn_memslot(slot, gfn, KVM_GTP_WRITE,
+				    NULL, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(gfn_to_pfn_memslot);
 
 kvm_pfn_t gfn_to_pfn_memslot_atomic(const struct kvm_memory_slot *slot, gfn_t gfn)
 {
-	return __gfn_to_pfn_memslot(slot, gfn, true, NULL, true, NULL, NULL);
+	return __gfn_to_pfn_memslot(slot, gfn, KVM_GTP_WRITE | KVM_GTP_ATOMIC,
+				    NULL, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(gfn_to_pfn_memslot_atomic);
 
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index 41da467d99c9..1c870911eb48 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -3,6 +3,8 @@
 #ifndef __KVM_MM_H__
 #define __KVM_MM_H__ 1
 
+#include <linux/kvm_host.h>
+
 /*
  * Architectures can choose whether to use an rwlock or spinlock
  * for the mmu_lock.  These macros, for use in common code
@@ -24,8 +26,8 @@
 #define KVM_MMU_READ_UNLOCK(kvm)	spin_unlock(&(kvm)->mmu_lock)
 #endif /* KVM_HAVE_MMU_RWLOCK */
 
-kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
-		     bool write_fault, bool *writable);
+kvm_pfn_t hva_to_pfn(unsigned long addr, kvm_gtp_flag_t gtp_flags, bool *async,
+		     bool *writable);
 
 #ifdef CONFIG_HAVE_KVM_PFNCACHE
 void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm,
diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c
index dd84676615f1..0f9f6b5d2fbb 100644
--- a/virt/kvm/pfncache.c
+++ b/virt/kvm/pfncache.c
@@ -123,7 +123,7 @@ static kvm_pfn_t hva_to_pfn_retry(struct kvm *kvm, unsigned long uhva)
 		smp_rmb();
 
 		/* We always request a writeable mapping */
-		new_pfn = hva_to_pfn(uhva, false, NULL, true, NULL);
+		new_pfn = hva_to_pfn(uhva, KVM_GTP_WRITE, NULL, NULL);
 		if (is_error_noslot_pfn(new_pfn))
 			break;
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH RFC 3/4] kvm: Add new pfn error KVM_PFN_ERR_INTR
  2022-06-17  1:41 [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot() Peter Xu
@ 2022-06-17  1:41 ` Peter Xu
  2022-06-17  1:41 ` [PATCH RFC 4/4] kvm/x86: Allow to respond to generic signals during slow page faults Peter Xu
  3 siblings, 0 replies; 8+ messages in thread
From: Peter Xu @ 2022-06-17  1:41 UTC (permalink / raw)
  To: kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton, peterx

Add one new PFN error type to show when we cannot finish fetching the PFN
due to interruptions.  For example, by receiving a generic signal.

This prepares KVM to be able to respond to SIGUSR1 (for QEMU that's the
SIGIPI) even during e.g. handling an userfaultfd page fault.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/linux/kvm_host.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b646b6fcaec6..4f84a442f67f 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -96,6 +96,7 @@
 #define KVM_PFN_ERR_FAULT	(KVM_PFN_ERR_MASK)
 #define KVM_PFN_ERR_HWPOISON	(KVM_PFN_ERR_MASK + 1)
 #define KVM_PFN_ERR_RO_FAULT	(KVM_PFN_ERR_MASK + 2)
+#define KVM_PFN_ERR_INTR	(KVM_PFN_ERR_MASK + 3)
 
 /*
  * error pfns indicate that the gfn is in slot but faild to
@@ -106,6 +107,16 @@ static inline bool is_error_pfn(kvm_pfn_t pfn)
 	return !!(pfn & KVM_PFN_ERR_MASK);
 }
 
+/*
+ * When KVM_PFN_ERR_INTR is returned, it means we're interrupted during
+ * fetching the PFN (e.g. a signal might have arrived), so we may want to
+ * retry at some later point and kick the userspace to handle the signal.
+ */
+static inline bool is_intr_pfn(kvm_pfn_t pfn)
+{
+	return pfn == KVM_PFN_ERR_INTR;
+}
+
 /*
  * error_noslot pfns indicate that the gfn can not be
  * translated to pfn - it is not in slot or failed to
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH RFC 4/4] kvm/x86: Allow to respond to generic signals during slow page faults
  2022-06-17  1:41 [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals Peter Xu
                   ` (2 preceding siblings ...)
  2022-06-17  1:41 ` [PATCH RFC 3/4] kvm: Add new pfn error KVM_PFN_ERR_INTR Peter Xu
@ 2022-06-17  1:41 ` Peter Xu
  3 siblings, 0 replies; 8+ messages in thread
From: Peter Xu @ 2022-06-17  1:41 UTC (permalink / raw)
  To: kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton, peterx

All the facilities should be ready for this, what we need to do is to add a
new KVM_GTP_INTERRUPTIBLE flag showing that we're willing to be interrupted
by common signals during the __gfn_to_pfn_memslot() request, and wire it up
with a FOLL_INTERRUPTIBLE flag that we've just introduced.

Note that only x86 slow page fault routine will set this new bit.  The new
bit is not used in non-x86 arch or on other gup paths even for x86.
However it can actually be used elsewhere too but not yet covered.

When we see the PFN fetching was interrupted, do early exit to userspace
with an KVM_EXIT_INTR exit reason.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 arch/x86/kvm/mmu/mmu.c   | 9 +++++++++
 include/linux/kvm_host.h | 1 +
 virt/kvm/kvm_main.c      | 4 ++++
 3 files changed, 14 insertions(+)

diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index e92f1ab63d6a..b39acb7cb16d 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -3012,6 +3012,13 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn)
 static int handle_abnormal_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
 			       unsigned int access)
 {
+	/* NOTE: not all error pfn is fatal; handle intr before the other ones */
+	if (unlikely(is_intr_pfn(fault->pfn))) {
+		vcpu->run->exit_reason = KVM_EXIT_INTR;
+		++vcpu->stat.signal_exits;
+		return -EINTR;
+	}
+
 	/* The pfn is invalid, report the error! */
 	if (unlikely(is_error_pfn(fault->pfn)))
 		return kvm_handle_bad_page(vcpu, fault->gfn, fault->pfn);
@@ -4017,6 +4024,8 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
 		}
 	}
 
+	/* Allow to respond to generic signals in slow page faults */
+	flags |= KVM_GTP_INTERRUPTIBLE;
 	fault->pfn = __gfn_to_pfn_memslot(slot, fault->gfn, flags, NULL,
 					  &fault->map_writable, &fault->hva);
 	return RET_PF_CONTINUE;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 4f84a442f67f..c8d98e435537 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1163,6 +1163,7 @@ typedef unsigned int __bitwise kvm_gtp_flag_t;
 
 #define  KVM_GTP_WRITE          ((__force kvm_gtp_flag_t) BIT(0))
 #define  KVM_GTP_ATOMIC         ((__force kvm_gtp_flag_t) BIT(1))
+#define  KVM_GTP_INTERRUPTIBLE  ((__force kvm_gtp_flag_t) BIT(2))
 
 kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn,
 			       kvm_gtp_flag_t gtp_flags, bool *async,
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 952400b42ee9..b3873cac5672 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2462,6 +2462,8 @@ static int hva_to_pfn_slow(unsigned long addr, bool *async,
 		flags |= FOLL_WRITE;
 	if (async)
 		flags |= FOLL_NOWAIT;
+	if (gtp_flags & KVM_GTP_INTERRUPTIBLE)
+		flags |= FOLL_INTERRUPTIBLE;
 
 	npages = get_user_pages_unlocked(addr, 1, &page, flags);
 	if (npages != 1)
@@ -2599,6 +2601,8 @@ kvm_pfn_t hva_to_pfn(unsigned long addr, kvm_gtp_flag_t gtp_flags, bool *async,
 	npages = hva_to_pfn_slow(addr, async, gtp_flags, writable, &pfn);
 	if (npages == 1)
 		return pfn;
+	if (npages == -EINTR)
+		return KVM_PFN_ERR_INTR;
 
 	mmap_read_lock(current->mm);
 	if (npages == -EHWPOISON ||
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot()
  2022-06-17  1:41 ` [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot() Peter Xu
@ 2022-06-17 21:53   ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-06-17 21:53 UTC (permalink / raw)
  To: Peter Xu; +Cc: llvm, kbuild-all

Hi Peter,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on powerpc/topic/ppc-kvm]
[also build test ERROR on mst-vhost/linux-next linus/master v5.19-rc2]
[cannot apply to kvm/queue next-20220617]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Peter-Xu/kvm-mm-Allow-GUP-to-respond-to-non-fatal-signals/20220617-094403
base:   https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git topic/ppc-kvm
config: arm64-randconfig-r001-20220617 (https://download.01.org/0day-ci/archive/20220618/202206180532.C71KuyHh-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project d764aa7fc6b9cc3fbe960019018f5f9e941eb0a6)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/6230b0019f9d1e0090102d9bb15c0029edf13c58
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Peter-Xu/kvm-mm-Allow-GUP-to-respond-to-non-fatal-signals/20220617-094403
        git checkout 6230b0019f9d1e0090102d9bb15c0029edf13c58
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> arch/arm64/kvm/mmu.c:1209:32: error: too many arguments to function call, expected 6, have 7
                                      false, NULL, &writable, NULL);
                                                              ^~~~
   include/linux/stddef.h:8:14: note: expanded from macro 'NULL'
   #define NULL ((void *)0)
                ^~~~~~~~~~~
   include/linux/kvm_host.h:1156:11: note: '__gfn_to_pfn_memslot' declared here
   kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn,
             ^
   1 error generated.


vim +1209 arch/arm64/kvm/mmu.c

  1086	
  1087	static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
  1088				  struct kvm_memory_slot *memslot, unsigned long hva,
  1089				  unsigned long fault_status)
  1090	{
  1091		int ret = 0;
  1092		bool write_fault, writable, force_pte = false;
  1093		bool exec_fault;
  1094		bool device = false;
  1095		bool shared;
  1096		unsigned long mmu_seq;
  1097		struct kvm *kvm = vcpu->kvm;
  1098		struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
  1099		struct vm_area_struct *vma;
  1100		short vma_shift;
  1101		gfn_t gfn;
  1102		kvm_pfn_t pfn;
  1103		bool logging_active = memslot_is_logging(memslot);
  1104		bool use_read_lock = false;
  1105		unsigned long fault_level = kvm_vcpu_trap_get_fault_level(vcpu);
  1106		unsigned long vma_pagesize, fault_granule;
  1107		enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
  1108		struct kvm_pgtable *pgt;
  1109	
  1110		fault_granule = 1UL << ARM64_HW_PGTABLE_LEVEL_SHIFT(fault_level);
  1111		write_fault = kvm_is_write_fault(vcpu);
  1112		exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
  1113		VM_BUG_ON(write_fault && exec_fault);
  1114	
  1115		if (fault_status == FSC_PERM && !write_fault && !exec_fault) {
  1116			kvm_err("Unexpected L2 read permission error\n");
  1117			return -EFAULT;
  1118		}
  1119	
  1120		/*
  1121		 * Let's check if we will get back a huge page backed by hugetlbfs, or
  1122		 * get block mapping for device MMIO region.
  1123		 */
  1124		mmap_read_lock(current->mm);
  1125		vma = vma_lookup(current->mm, hva);
  1126		if (unlikely(!vma)) {
  1127			kvm_err("Failed to find VMA for hva 0x%lx\n", hva);
  1128			mmap_read_unlock(current->mm);
  1129			return -EFAULT;
  1130		}
  1131	
  1132		/*
  1133		 * logging_active is guaranteed to never be true for VM_PFNMAP
  1134		 * memslots.
  1135		 */
  1136		if (logging_active) {
  1137			force_pte = true;
  1138			vma_shift = PAGE_SHIFT;
  1139			use_read_lock = (fault_status == FSC_PERM && write_fault &&
  1140					 fault_granule == PAGE_SIZE);
  1141		} else {
  1142			vma_shift = get_vma_page_shift(vma, hva);
  1143		}
  1144	
  1145		shared = (vma->vm_flags & VM_SHARED);
  1146	
  1147		switch (vma_shift) {
  1148	#ifndef __PAGETABLE_PMD_FOLDED
  1149		case PUD_SHIFT:
  1150			if (fault_supports_stage2_huge_mapping(memslot, hva, PUD_SIZE))
  1151				break;
  1152			fallthrough;
  1153	#endif
  1154		case CONT_PMD_SHIFT:
  1155			vma_shift = PMD_SHIFT;
  1156			fallthrough;
  1157		case PMD_SHIFT:
  1158			if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE))
  1159				break;
  1160			fallthrough;
  1161		case CONT_PTE_SHIFT:
  1162			vma_shift = PAGE_SHIFT;
  1163			force_pte = true;
  1164			fallthrough;
  1165		case PAGE_SHIFT:
  1166			break;
  1167		default:
  1168			WARN_ONCE(1, "Unknown vma_shift %d", vma_shift);
  1169		}
  1170	
  1171		vma_pagesize = 1UL << vma_shift;
  1172		if (vma_pagesize == PMD_SIZE || vma_pagesize == PUD_SIZE)
  1173			fault_ipa &= ~(vma_pagesize - 1);
  1174	
  1175		gfn = fault_ipa >> PAGE_SHIFT;
  1176		mmap_read_unlock(current->mm);
  1177	
  1178		/*
  1179		 * Permission faults just need to update the existing leaf entry,
  1180		 * and so normally don't require allocations from the memcache. The
  1181		 * only exception to this is when dirty logging is enabled at runtime
  1182		 * and a write fault needs to collapse a block entry into a table.
  1183		 */
  1184		if (fault_status != FSC_PERM || (logging_active && write_fault)) {
  1185			ret = kvm_mmu_topup_memory_cache(memcache,
  1186							 kvm_mmu_cache_min_pages(kvm));
  1187			if (ret)
  1188				return ret;
  1189		}
  1190	
  1191		mmu_seq = vcpu->kvm->mmu_notifier_seq;
  1192		/*
  1193		 * Ensure the read of mmu_notifier_seq happens before we call
  1194		 * gfn_to_pfn_prot (which calls get_user_pages), so that we don't risk
  1195		 * the page we just got a reference to gets unmapped before we have a
  1196		 * chance to grab the mmu_lock, which ensure that if the page gets
  1197		 * unmapped afterwards, the call to kvm_unmap_gfn will take it away
  1198		 * from us again properly. This smp_rmb() interacts with the smp_wmb()
  1199		 * in kvm_mmu_notifier_invalidate_<page|range_end>.
  1200		 *
  1201		 * Besides, __gfn_to_pfn_memslot() instead of gfn_to_pfn_prot() is
  1202		 * used to avoid unnecessary overhead introduced to locate the memory
  1203		 * slot because it's always fixed even @gfn is adjusted for huge pages.
  1204		 */
  1205		smp_rmb();
  1206	
  1207		pfn = __gfn_to_pfn_memslot(memslot, gfn,
  1208					   write_fault ? KVM_GTP_WRITE : 0,
> 1209					   false, NULL, &writable, NULL);
  1210		if (pfn == KVM_PFN_ERR_HWPOISON) {
  1211			kvm_send_hwpoison_signal(hva, vma_shift);
  1212			return 0;
  1213		}
  1214		if (is_error_noslot_pfn(pfn))
  1215			return -EFAULT;
  1216	
  1217		if (kvm_is_device_pfn(pfn)) {
  1218			/*
  1219			 * If the page was identified as device early by looking at
  1220			 * the VMA flags, vma_pagesize is already representing the
  1221			 * largest quantity we can map.  If instead it was mapped
  1222			 * via gfn_to_pfn_prot(), vma_pagesize is set to PAGE_SIZE
  1223			 * and must not be upgraded.
  1224			 *
  1225			 * In both cases, we don't let transparent_hugepage_adjust()
  1226			 * change things at the last minute.
  1227			 */
  1228			device = true;
  1229		} else if (logging_active && !write_fault) {
  1230			/*
  1231			 * Only actually map the page as writable if this was a write
  1232			 * fault.
  1233			 */
  1234			writable = false;
  1235		}
  1236	
  1237		if (exec_fault && device)
  1238			return -ENOEXEC;
  1239	
  1240		/*
  1241		 * To reduce MMU contentions and enhance concurrency during dirty
  1242		 * logging dirty logging, only acquire read lock for permission
  1243		 * relaxation.
  1244		 */
  1245		if (use_read_lock)
  1246			read_lock(&kvm->mmu_lock);
  1247		else
  1248			write_lock(&kvm->mmu_lock);
  1249		pgt = vcpu->arch.hw_mmu->pgt;
  1250		if (mmu_notifier_retry(kvm, mmu_seq))
  1251			goto out_unlock;
  1252	
  1253		/*
  1254		 * If we are not forced to use page mapping, check if we are
  1255		 * backed by a THP and thus use block mapping if possible.
  1256		 */
  1257		if (vma_pagesize == PAGE_SIZE && !(force_pte || device)) {
  1258			if (fault_status == FSC_PERM && fault_granule > PAGE_SIZE)
  1259				vma_pagesize = fault_granule;
  1260			else
  1261				vma_pagesize = transparent_hugepage_adjust(kvm, memslot,
  1262									   hva, &pfn,
  1263									   &fault_ipa);
  1264		}
  1265	
  1266		if (fault_status != FSC_PERM && !device && kvm_has_mte(kvm)) {
  1267			/* Check the VMM hasn't introduced a new VM_SHARED VMA */
  1268			if (!shared)
  1269				ret = sanitise_mte_tags(kvm, pfn, vma_pagesize);
  1270			else
  1271				ret = -EFAULT;
  1272			if (ret)
  1273				goto out_unlock;
  1274		}
  1275	
  1276		if (writable)
  1277			prot |= KVM_PGTABLE_PROT_W;
  1278	
  1279		if (exec_fault)
  1280			prot |= KVM_PGTABLE_PROT_X;
  1281	
  1282		if (device)
  1283			prot |= KVM_PGTABLE_PROT_DEVICE;
  1284		else if (cpus_have_const_cap(ARM64_HAS_CACHE_DIC))
  1285			prot |= KVM_PGTABLE_PROT_X;
  1286	
  1287		/*
  1288		 * Under the premise of getting a FSC_PERM fault, we just need to relax
  1289		 * permissions only if vma_pagesize equals fault_granule. Otherwise,
  1290		 * kvm_pgtable_stage2_map() should be called to change block size.
  1291		 */
  1292		if (fault_status == FSC_PERM && vma_pagesize == fault_granule) {
  1293			ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
  1294		} else {
  1295			WARN_ONCE(use_read_lock, "Attempted stage-2 map outside of write lock\n");
  1296	
  1297			ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,
  1298						     __pfn_to_phys(pfn), prot,
  1299						     memcache);
  1300		}
  1301	
  1302		/* Mark the page dirty only if the fault is handled successfully */
  1303		if (writable && !ret) {
  1304			kvm_set_pfn_dirty(pfn);
  1305			mark_page_dirty_in_slot(kvm, memslot, gfn);
  1306		}
  1307	
  1308	out_unlock:
  1309		if (use_read_lock)
  1310			read_unlock(&kvm->mmu_lock);
  1311		else
  1312			write_unlock(&kvm->mmu_lock);
  1313		kvm_set_pfn_accessed(pfn);
  1314		kvm_release_pfn_clean(pfn);
  1315		return ret != -EAGAIN ? ret : 0;
  1316	}
  1317	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE
  2022-06-17  1:41 ` [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE Peter Xu
@ 2022-06-21  8:23   ` David Hildenbrand
  2022-06-21 17:09     ` Peter Xu
  0 siblings, 1 reply; 8+ messages in thread
From: David Hildenbrand @ 2022-06-21  8:23 UTC (permalink / raw)
  To: Peter Xu, kvm, linux-kernel
  Cc: Dr . David Alan Gilbert, Linux MM Mailing List,
	Sean Christopherson, Paolo Bonzini, Andrea Arcangeli,
	Andrew Morton

On 17.06.22 03:41, Peter Xu wrote:
> We have had FAULT_FLAG_INTERRUPTIBLE but it was never applied to GUPs.  One
> issue with it is that not all GUP paths are able to handle signal delivers
> besides SIGKILL.
> 
> That's not ideal for the GUP users who are actually able to handle these
> cases, like KVM.
> 
> KVM uses GUP extensively on faulting guest pages, during which we've got
> existing infrastructures to retry a page fault at a later time.  Allowing
> the GUP to be interrupted by generic signals can make KVM related threads
> to be more responsive.  For examples:
> 
>   (1) SIGUSR1: which QEMU/KVM uses to deliver an inter-process IPI,
>       e.g. when the admin issues a vm_stop QMP command, SIGUSR1 can be
>       generated to kick the vcpus out of kernel context immediately,
> 
>   (2) SIGINT: which can be used with interactive hypervisor users to stop a
>       virtual machine with Ctrl-C without any delays/hangs,
> 
>   (3) SIGTRAP: which grants GDB capability even during page faults that are
>       stuck for a long time.
> 
> Normally hypervisor will be able to receive these signals properly, but not
> if we're stuck in a GUP for a long time for whatever reason.  It happens
> easily with a stucked postcopy migration when e.g. a network temp failure
> happens, then some vcpu threads can hang death waiting for the pages.  With
> the new FOLL_INTERRUPTIBLE, we can allow GUP users like KVM to selectively
> enable the ability to trap these signals.

This makes sense to me. I assume relevant callers will detect "GUP
failed" but also "well, there is a signal to handle" and cleanly back
off, correct?

-- 
Thanks,

David / dhildenb


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE
  2022-06-21  8:23   ` David Hildenbrand
@ 2022-06-21 17:09     ` Peter Xu
  0 siblings, 0 replies; 8+ messages in thread
From: Peter Xu @ 2022-06-21 17:09 UTC (permalink / raw)
  To: David Hildenbrand
  Cc: kvm, linux-kernel, Dr . David Alan Gilbert,
	Linux MM Mailing List, Sean Christopherson, Paolo Bonzini,
	Andrea Arcangeli, Andrew Morton

On Tue, Jun 21, 2022 at 10:23:32AM +0200, David Hildenbrand wrote:
> On 17.06.22 03:41, Peter Xu wrote:
> > We have had FAULT_FLAG_INTERRUPTIBLE but it was never applied to GUPs.  One
> > issue with it is that not all GUP paths are able to handle signal delivers
> > besides SIGKILL.
> > 
> > That's not ideal for the GUP users who are actually able to handle these
> > cases, like KVM.
> > 
> > KVM uses GUP extensively on faulting guest pages, during which we've got
> > existing infrastructures to retry a page fault at a later time.  Allowing
> > the GUP to be interrupted by generic signals can make KVM related threads
> > to be more responsive.  For examples:
> > 
> >   (1) SIGUSR1: which QEMU/KVM uses to deliver an inter-process IPI,
> >       e.g. when the admin issues a vm_stop QMP command, SIGUSR1 can be
> >       generated to kick the vcpus out of kernel context immediately,
> > 
> >   (2) SIGINT: which can be used with interactive hypervisor users to stop a
> >       virtual machine with Ctrl-C without any delays/hangs,
> > 
> >   (3) SIGTRAP: which grants GDB capability even during page faults that are
> >       stuck for a long time.
> > 
> > Normally hypervisor will be able to receive these signals properly, but not
> > if we're stuck in a GUP for a long time for whatever reason.  It happens
> > easily with a stucked postcopy migration when e.g. a network temp failure
> > happens, then some vcpu threads can hang death waiting for the pages.  With
> > the new FOLL_INTERRUPTIBLE, we can allow GUP users like KVM to selectively
> > enable the ability to trap these signals.
> 
> This makes sense to me. I assume relevant callers will detect "GUP
> failed" but also "well, there is a signal to handle" and cleanly back
> off, correct?

Correct, via an -EINTR.

One thing to mention is that the gup user behavior will be the same as
before if the caller didn't explicilty pass in FOLL_INTERRUPTIBLE with the
gup call.  So after the whole series applied only kvm (and only some path
of kvm, not all GUP; I only touched up the x86 slow page fault path) to
handle this, but that'll be far enough to cover 99.99% use cases that I
wanted to take care of.

E.g., some kvm request to gup on some guest apic page may not still be able
to respond to a SIGUSR1 but that's very very rare, and we can always add
more users of FOLL_INTERRUPTIBLE when the code is ready to benefit from the
fast respondings.

Thanks,

-- 
Peter Xu


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-06-21 17:09 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-17  1:41 [PATCH RFC 0/4] kvm/mm: Allow GUP to respond to non fatal signals Peter Xu
2022-06-17  1:41 ` [PATCH RFC 1/4] mm/gup: Add FOLL_INTERRUPTIBLE Peter Xu
2022-06-21  8:23   ` David Hildenbrand
2022-06-21 17:09     ` Peter Xu
2022-06-17  1:41 ` [PATCH RFC 2/4] kvm: Merge "atomic" and "write" in __gfn_to_pfn_memslot() Peter Xu
2022-06-17 21:53   ` kernel test robot
2022-06-17  1:41 ` [PATCH RFC 3/4] kvm: Add new pfn error KVM_PFN_ERR_INTR Peter Xu
2022-06-17  1:41 ` [PATCH RFC 4/4] kvm/x86: Allow to respond to generic signals during slow page faults Peter Xu

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.