Re: [PATCH v6 3/8] KVM: Add support for using dirty ring in conjunction with bitmap

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Peter,

On 10/19/22 12:07 AM, Peter Xu wrote:
On Tue, Oct 11, 2022 at 02:14:42PM +0800, Gavin Shan wrote:
Some architectures (such as arm64) need to dirty memory outside of the
context of a vCPU. Of course, this simply doesn't fit with the UAPI of
KVM's per-vCPU dirty ring.

Introduce a new flavor of dirty ring that requires the use of both vCPU
dirty rings and a dirty bitmap. The expectation is that for non-vCPU
sources of dirty memory (such as the GIC ITS on arm64), KVM writes to
the dirty bitmap. Userspace should scan the dirty bitmap before
migrating the VM to the target.

Use an additional capability to advertize this behavior and require
explicit opt-in to avoid breaking the existing dirty ring ABI. And yes,
you can use this with your preferred flavor of DIRTY_RING[_ACQ_REL]. Do
not allow userspace to enable dirty ring if it hasn't also enabled the
ring && bitmap capability, as a VM is likely DOA without the pages
marked in the bitmap.

Suggested-by: Marc Zyngier <maz@xxxxxxxxxx>
Suggested-by: Peter Xu <peterx@xxxxxxxxxx>
Co-developed-by: Oliver Upton <oliver.upton@xxxxxxxxx>
Signed-off-by: Gavin Shan <gshan@xxxxxxxxxx>
---
  Documentation/virt/kvm/api.rst | 17 ++++++++---------
  include/linux/kvm_dirty_ring.h |  6 ++++++
  include/linux/kvm_host.h       |  1 +
  include/uapi/linux/kvm.h       |  1 +
  virt/kvm/Kconfig               |  8 ++++++++
  virt/kvm/dirty_ring.c          |  5 +++++
  virt/kvm/kvm_main.c            | 34 +++++++++++++++++++++++++---------
  7 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 32427ea160df..09fa6c491c1b 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -8019,8 +8019,8 @@ guest according to the bits in the KVM_CPUID_FEATURES CPUID leaf
  (0x40000001). Otherwise, a guest may use the paravirtual features
  regardless of what has actually been exposed through the CPUID leaf.
-8.29 KVM_CAP_DIRTY_LOG_RING/KVM_CAP_DIRTY_LOG_RING_ACQ_REL
-----------------------------------------------------------
+8.29 KVM_CAP_DIRTY_LOG_{RING, RING_ACQ_REL, RING_WITH_BITMAP}

Shall we open a new section for RING_WITH_BITMAP?  Otherwise here it still
looks like these are three options for the rings.

Perhaps RING_WITH_BITMAP doesn't worth a section at all, so we can avoid
mentioning it here to avoid confusing.


Lets avoid mentioning it in the subject since it's a add-on functionality
and not all architectures need it.

+-------------------------------------------------------------
:Architectures: x86
  :Parameters: args[0] - size of the dirty log ring
@@ -8104,13 +8104,6 @@ flushing is done by the KVM_GET_DIRTY_LOG ioctl).  To achieve that, one
  needs to kick the vcpu out of KVM_RUN using a signal.  The resulting
  vmexit ensures that all dirty GFNs are flushed to the dirty rings.
-NOTE: the capability KVM_CAP_DIRTY_LOG_RING and the corresponding
-ioctl KVM_RESET_DIRTY_RINGS are mutual exclusive to the existing ioctls
-KVM_GET_DIRTY_LOG and KVM_CLEAR_DIRTY_LOG.  After enabling
-KVM_CAP_DIRTY_LOG_RING with an acceptable dirty ring size, the virtual
-machine will switch to ring-buffer dirty page tracking and further
-KVM_GET_DIRTY_LOG or KVM_CLEAR_DIRTY_LOG ioctls will fail.
-
  NOTE: KVM_CAP_DIRTY_LOG_RING_ACQ_REL is the only capability that
  should be exposed by weakly ordered architecture, in order to indicate
  the additional memory ordering requirements imposed on userspace when
@@ -8119,6 +8112,12 @@ Architecture with TSO-like ordering (such as x86) are allowed to
  expose both KVM_CAP_DIRTY_LOG_RING and KVM_CAP_DIRTY_LOG_RING_ACQ_REL
  to userspace.
+NOTE: There is no running vcpu and available vcpu dirty ring when pages

IMHO it'll be great to start with something like below to describe the
userspace's responsibility to proactively detect the WITH_BITMAP cap:

   Before using the dirty rings, the userspace needs to detect the cap of
   KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP to see whether the ring structures
   need to be backed by per-slot bitmaps.

   When KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP returns 1, it means the arch can
   dirty guest pages without vcpu/ring context, so that some of the dirty
   information will still be maintained in the bitmap structure.

   Note that the bitmap here is only a backup of the ring structure, and it
   doesn't need to be collected until the final switch-over of migration
   process.  Normally the bitmap should only contain a very small amount of
   dirty pages only, which needs to be transferred during VM downtime.

   To collect dirty bits in the backup bitmap, the userspace can use the
   same KVM_GET_DIRTY_LOG ioctl.  Since it's always the last phase of
   migration that needs the fetching of dirty bitmap, KVM_CLEAR_DIRTY_LOG
   ioctl should not be needed in this case and its behavior undefined.

That's how I understand this new cap, but let me know if you think any of
above is inproper.


Yes, It looks much better to describe how KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP
is used. However, the missed part is the capability is still need to be enabled
prior to KVM_CAP_DIRTY_LOG_RING_ACQ_REL on ARM64. It means the capability needs
to be acknowledged (confirmed) by user space. Otherwise, KVM_CAP_DIRTY_LOG_RING_ACQ_REL
can't be enabled successfully. It seems Oliver, you and I aren't on same page for
this part. Please refer to below reply for more discussion. After the discussion
is finalized, I can amend the description accordingly here.

+becomes dirty in some cases. One example is to save arm64's vgic/its
+tables during migration.

Nit: it'll be great to mention the exact arm ioctl here just in case anyone
would like to further reference the code.


Yep, good point. I will mention the ioctl here.

The dirty bitmap is still used to track those
+dirty pages, indicated by KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP. The ditry
+bitmap is visited by KVM_GET_DIRTY_LOG and KVM_CLEAR_DIRTY_LOG ioctls.
+
  8.30 KVM_CAP_XEN_HVM
  --------------------
diff --git a/include/linux/kvm_dirty_ring.h b/include/linux/kvm_dirty_ring.h
index fe5982b46424..23b2b466aa0f 100644
--- a/include/linux/kvm_dirty_ring.h
+++ b/include/linux/kvm_dirty_ring.h
@@ -28,6 +28,11 @@ struct kvm_dirty_ring {
  };
#ifndef CONFIG_HAVE_KVM_DIRTY_RING
+static inline bool kvm_dirty_ring_exclusive(struct kvm *kvm)
+{
+	return false;
+}
+
  /*
   * If CONFIG_HAVE_HVM_DIRTY_RING not defined, kvm_dirty_ring.o should
   * not be included as well, so define these nop functions for the arch.
@@ -66,6 +71,7 @@ static inline void kvm_dirty_ring_free(struct kvm_dirty_ring *ring)
#else /* CONFIG_HAVE_KVM_DIRTY_RING */ +bool kvm_dirty_ring_exclusive(struct kvm *kvm);
  int kvm_cpu_dirty_log_size(void);
  u32 kvm_dirty_ring_get_rsvd_entries(void);
  int kvm_dirty_ring_alloc(struct kvm_dirty_ring *ring, int index, u32 size);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 53fa3134fee0..a3fae111f25c 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -780,6 +780,7 @@ struct kvm {
  	pid_t userspace_pid;
  	unsigned int max_halt_poll_ns;
  	u32 dirty_ring_size;
+	bool dirty_ring_with_bitmap;
  	bool vm_bugged;
  	bool vm_dead;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 0d5d4419139a..c87b5882d7ae 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1178,6 +1178,7 @@ struct kvm_ppc_resize_hpt {
  #define KVM_CAP_S390_ZPCI_OP 221
  #define KVM_CAP_S390_CPU_TOPOLOGY 222
  #define KVM_CAP_DIRTY_LOG_RING_ACQ_REL 223
+#define KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP 224
#ifdef KVM_CAP_IRQ_ROUTING diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 800f9470e36b..228be1145cf3 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -33,6 +33,14 @@ config HAVE_KVM_DIRTY_RING_ACQ_REL
         bool
         select HAVE_KVM_DIRTY_RING
+# Only architectures that need to dirty memory outside of a vCPU
+# context should select this, advertising to userspace the
+# requirement to use a dirty bitmap in addition to the vCPU dirty
+# ring.
+config HAVE_KVM_DIRTY_RING_WITH_BITMAP
+	bool
+	depends on HAVE_KVM_DIRTY_RING
+
  config HAVE_KVM_EVENTFD
         bool
         select EVENTFD
diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c
index f68d75026bc0..9cc60af291ef 100644
--- a/virt/kvm/dirty_ring.c
+++ b/virt/kvm/dirty_ring.c
@@ -11,6 +11,11 @@
  #include <trace/events/kvm.h>
  #include "kvm_mm.h"
+bool kvm_dirty_ring_exclusive(struct kvm *kvm)
+{
+	return kvm->dirty_ring_size && !kvm->dirty_ring_with_bitmap;
+}
+
  int __weak kvm_cpu_dirty_log_size(void)
  {
  	return 0;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 5b064dbadaf4..8915dcefcefd 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1617,7 +1617,7 @@ static int kvm_prepare_memory_region(struct kvm *kvm,
  			new->dirty_bitmap = NULL;
  		else if (old && old->dirty_bitmap)
  			new->dirty_bitmap = old->dirty_bitmap;
-		else if (!kvm->dirty_ring_size) {
+		else if (!kvm_dirty_ring_exclusive(kvm)) {
  			r = kvm_alloc_dirty_bitmap(new);
  			if (r)
  				return r;
@@ -2060,8 +2060,8 @@ int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log,
  	unsigned long n;
  	unsigned long any = 0;
- /* Dirty ring tracking is exclusive to dirty log tracking */
-	if (kvm->dirty_ring_size)
+	/* Dirty ring tracking may be exclusive to dirty log tracking */
+	if (kvm_dirty_ring_exclusive(kvm))
  		return -ENXIO;
*memslot = NULL;
@@ -2125,8 +2125,8 @@ static int kvm_get_dirty_log_protect(struct kvm *kvm, struct kvm_dirty_log *log)
  	unsigned long *dirty_bitmap_buffer;
  	bool flush;
- /* Dirty ring tracking is exclusive to dirty log tracking */
-	if (kvm->dirty_ring_size)
+	/* Dirty ring tracking may be exclusive to dirty log tracking */
+	if (kvm_dirty_ring_exclusive(kvm))
  		return -ENXIO;
as_id = log->slot >> 16;
@@ -2237,8 +2237,8 @@ static int kvm_clear_dirty_log_protect(struct kvm *kvm,
  	unsigned long *dirty_bitmap_buffer;
  	bool flush;
- /* Dirty ring tracking is exclusive to dirty log tracking */
-	if (kvm->dirty_ring_size)
+	/* Dirty ring tracking may be exclusive to dirty log tracking */
+	if (kvm_dirty_ring_exclusive(kvm))
  		return -ENXIO;
as_id = log->slot >> 16;
@@ -3305,15 +3305,20 @@ void mark_page_dirty_in_slot(struct kvm *kvm,
  	struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
#ifdef CONFIG_HAVE_KVM_DIRTY_RING
-	if (WARN_ON_ONCE(!vcpu) || WARN_ON_ONCE(vcpu->kvm != kvm))
+	if (WARN_ON_ONCE(vcpu && vcpu->kvm != kvm))
  		return;
+
+#ifndef CONFIG_HAVE_KVM_DIRTY_RING_WITH_BITMAP
+	if (WARN_ON_ONCE(!vcpu))
+		return;
+#endif
  #endif
if (memslot && kvm_slot_dirty_track_enabled(memslot)) {
  		unsigned long rel_gfn = gfn - memslot->base_gfn;
  		u32 slot = (memslot->as_id << 16) | memslot->id;
- if (kvm->dirty_ring_size)
+		if (vcpu && kvm->dirty_ring_size)
  			kvm_dirty_ring_push(&vcpu->dirty_ring,
  					    slot, rel_gfn);
  		else
@@ -4485,6 +4490,9 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
  		return KVM_DIRTY_RING_MAX_ENTRIES * sizeof(struct kvm_dirty_gfn);
  #else
  		return 0;
+#endif
+#ifdef CONFIG_HAVE_KVM_DIRTY_RING_WITH_BITMAP
+	case KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP:
  #endif
  	case KVM_CAP_BINARY_STATS_FD:
  	case KVM_CAP_SYSTEM_EVENT_DATA:
@@ -4499,6 +4507,11 @@ static int kvm_vm_ioctl_enable_dirty_log_ring(struct kvm *kvm, u32 size)
  {
  	int r;
+#ifdef CONFIG_HAVE_KVM_DIRTY_RING_WITH_BITMAP
+	if (!kvm->dirty_ring_with_bitmap)
+		return -EINVAL;
+#endif
+
  	if (!KVM_DIRTY_LOG_PAGE_OFFSET)
  		return -EINVAL;
@@ -4588,6 +4601,9 @@ static int kvm_vm_ioctl_enable_cap_generic(struct kvm *kvm,
  	case KVM_CAP_DIRTY_LOG_RING:
  	case KVM_CAP_DIRTY_LOG_RING_ACQ_REL:
  		return kvm_vm_ioctl_enable_dirty_log_ring(kvm, cap->args[0]);
+	case KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP:
+		kvm->dirty_ring_with_bitmap = true;

IIUC what Oliver wanted to suggest is we can avoid enabling of this cap,
then we don't need dirty_ring_with_bitmap field but instead we can check
against CONFIG_HAVE_KVM_DIRTY_RING_WITH_BITMAP when needed.

I think that'll make sense, because without the bitmap the ring won't work
with arm64, so not valid to not enable it at all.  But good to double check
with Oliver too.

The rest looks good to me, thanks,


It was suggested by Oliver to expose KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP. The
user space also needs to enable the capability prior to KVM_CAP_DIRTY_LOG_RING_ACQ_REL
on ARM64. I may be missing something since Oliver and you had lots of discussion
on this particular new capability.

I'm fine to drop the bits to enable KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP. It means
the capability is exposed to user space on ARM64 and user space need __not__ to
enable it prior to KVM_CAP_DIRTY_LOG_RING_ACQ_REL. I would like Oliver helps to
confirm before I'm able to post v7.

+		return 0;
  	default:
  		return kvm_vm_ioctl_enable_cap(kvm, cap);
  	}

Thanks,
Gavin




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux