[PATCH RFC v2 6/6] KVM: introduce a new API for getting dirty bitmaps

Takuya Yoshikawa
2010-04-20T11:03:53Z

We can now export the addree of the bitmap created by do_mmap()
to user space. For the sake of this, we introduce a new API:

KVM_SWITCH_DIRTY_LOG: application can use this to trigger the
switch of the bitmaps and get the address of the bitmap which
has been used until now. This reduces the copy of the dirty
bitmap from the kernel.

Signed-off-by: Takuya Yoshikawa <yoshikawa.takuya-gVGce1chcLdL9jVzuh4AOg@public.gmane.org>
Cc: Fernando Luis Vazquez Cao <fernando-gVGce1chcLdL9jVzuh4AOg@public.gmane.org>
---
Documentation/kvm/api.txt | 23 +++++++++++++++++++++++
arch/ia64/kvm/kvm-ia64.c | 19 ++++++++++++++-----
arch/powerpc/kvm/book3s.c | 19 ++++++++++++++-----
arch/x86/kvm/x86.c | 32 ++++++++++++++++++++++++++------
include/linux/kvm.h | 6 ++++--
include/linux/kvm_host.h | 7 ++++---
virt/kvm/kvm_main.c | 41 ++++++++++++++++++++++++++++++++++++-----
7 files changed, 121 insertions(+), 26 deletions(-)

diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt
index baa8fde..f3d1c2a 100644
--- a/Documentation/kvm/api.txt
+++ b/Documentation/kvm/api.txt
@@ -848,6 +848,29 @@ function properly, this is the place to put them.
__u8 pad[64];
};

+4.37 KVM_SWITCH_DIRTY_LOG (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_dirty_log (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_SWITCH_DIRTY_LOG */
+struct kvm_dirty_log {
+ __u32 slot;
+ __u32 padding;
+ union {
+ void __user *dirty_bitmap; /* one bit per page */
+ __u64 addr;
+ };
+};
+
+Given a memory slot, return the address of a bitmap containing any
+pages dirtied since the last call to this ioctl. Bit 0 is the first
+page in the memory slot. Ensure the entire structure is cleared to
+avoid padding issues.
+
5. The kvm_run structure

Application code obtains a pointer to the kvm_run structure by
diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c
index c3f0b70..04fbb1c 100644
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -1843,8 +1843,8 @@ out:
return r;
}

-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
- struct kvm_dirty_log *log)
+static int kvm_ia64_update_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log,
+ bool need_copy)
{
int r;
unsigned long n;
@@ -1857,7 +1857,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
if (r)
goto out;

- r = kvm_get_dirty_log(kvm, log);
+ r = kvm_update_dirty_log(kvm, log, need_copy);
if (r)
goto out;

@@ -1865,10 +1865,9 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
/* If nothing is dirty, don't bother messing with page tables. */
if (memslot->is_dirty) {
kvm_flush_remote_tlbs(kvm);
- n = kvm_dirty_bitmap_bytes(memslot);
- clear_user(memslot->dirty_bitmap, n);
memslot->is_dirty = false;
}
+
r = 0;
out:
mutex_unlock(&kvm->slots_lock);
@@ -1876,6 +1875,16 @@ out:
return r;
}

+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvm_ia64_update_dirty_log(kvm, log, true);
+}
+
+int kvm_vm_ioctl_switch_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvm_ia64_update_dirty_log(kvm, log, false);
+}
+
int kvm_arch_hardware_setup(void)
{
return 0;
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
index b074e19..d813dca 100644
--- a/arch/powerpc/kvm/book3s.c
+++ b/arch/powerpc/kvm/book3s.c
@@ -1112,8 +1112,9 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
/*
* Get (and clear) the dirty memory log for a memory slot.
*/
-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
- struct kvm_dirty_log *log)
+static int kvmppc_update_dirty_log(struct kvm *kvm,
+ struct kvm_dirty_log *log,
+ bool need_copy)
{
struct kvm_memory_slot *memslot;
struct kvm_vcpu *vcpu;
@@ -1123,7 +1124,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,

mutex_lock(&kvm->slots_lock);

- r = kvm_get_dirty_log(kvm, log);
+ r = kvm_update_dirty_log(kvm, log, need_copy);
if (r)
goto out;

@@ -1136,8 +1137,6 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
kvm_for_each_vcpu(n, vcpu, kvm)
kvmppc_mmu_pte_pflush(vcpu, ga, ga_end);

- n = kvm_dirty_bitmap_bytes(memslot);
- clear_user(memslot->dirty_bitmap, n);
memslot->is_dirty = false;
}

@@ -1147,6 +1146,16 @@ out:
return r;
}

+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvmppc_update_dirty_log(kvm, log, true);
+}
+
+int kvm_vm_ioctl_switch_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvmppc_update_dirty_log(kvm, log, false);
+}
+
int kvmppc_core_check_processor_compat(void)
{
return 0;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ad55353..45b728c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2718,11 +2718,9 @@ static int kvm_vm_ioctl_reinject(struct kvm *kvm,
return 0;
}

-/*
- * Get (and clear) the dirty memory log for a memory slot.
- */
-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
- struct kvm_dirty_log *log)
+static int kvm_x86_update_dirty_log(struct kvm *kvm,
+ struct kvm_dirty_log *log,
+ bool need_copy)
{
int r;
struct kvm_memory_slot *memslot;
@@ -2773,12 +2771,34 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
dirty_bitmap_old = dirty_bitmap;
}

- r = kvm_copy_dirty_bitmap(log->dirty_bitmap, dirty_bitmap_old, n);
+ if (need_copy) {
+ r = kvm_copy_dirty_bitmap(log->dirty_bitmap,
+ dirty_bitmap_old, n);
+ } else {
+ log->addr = (unsigned long)dirty_bitmap_old;
+ r = 0;
+ }
out:
mutex_unlock(&kvm->slots_lock);
return r;
}

+/*
+ * Get (and clear) the dirty memory log for a memory slot.
+ */
+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvm_x86_update_dirty_log(kvm, log, true);
+}
+
+/*
+ * Switch to the next dirty bitmap and return the address of the old one.
+ */
+int kvm_vm_ioctl_switch_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+{
+ return kvm_x86_update_dirty_log(kvm, log, false);
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 23ea022..9fa6f1e 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -12,7 +12,7 @@
#include <linux/ioctl.h>
#include <asm/kvm.h>

-#define KVM_API_VERSION 12
+#define KVM_API_VERSION 13

/* *** Deprecated interfaces *** */

@@ -318,7 +318,7 @@ struct kvm_dirty_log {
__u32 padding1;
union {
void __user *dirty_bitmap; /* one bit per page */
- __u64 padding2;
+ __u64 addr;
};
};

@@ -524,6 +524,7 @@ struct kvm_enable_cap {
#define KVM_CAP_PPC_OSI 52
#define KVM_CAP_PPC_UNSET_IRQ 53
#define KVM_CAP_ENABLE_CAP 54
+#define KVM_CAP_USER_DIRTY_BITMAP 55

#ifdef KVM_CAP_IRQ_ROUTING

@@ -620,6 +621,7 @@ struct kvm_clock_data {
struct kvm_userspace_memory_region)
#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47)
#define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64)
+#define KVM_SWITCH_DIRTY_LOG _IOW(KVMIO, 0x49, struct kvm_dirty_log)
/* Device model IOC */
#define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60)
#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d4d217a..addecee 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -334,9 +334,10 @@ int kvm_dev_ioctl_check_extension(long ext);
int kvm_copy_dirty_bitmap(unsigned long __user *to,
const unsigned long __user *from,
unsigned long bytes);
-int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log);
-int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
- struct kvm_dirty_log *log);
+int kvm_update_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log,
+ bool need_copy);
+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log);
+int kvm_vm_ioctl_switch_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log);

int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
struct
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 9323639..cba133d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -844,7 +844,8 @@ out_fault:
#endif
}

-int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
+int kvm_update_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log,
+ bool need_copy)
{
struct kvm_memory_slot *memslot;
int r;
@@ -861,9 +862,23 @@ int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)

n = kvm_dirty_bitmap_bytes(memslot);

- r = -EFAULT;
- if (kvm_copy_dirty_bitmap(log->dirty_bitmap, memslot->dirty_bitmap, n))
- goto out;
+ if (need_copy) {
+ r = -EFAULT;
+ if (kvm_copy_dirty_bitmap(log->dirty_bitmap,
+ memslot->dirty_bitmap, n))
+ goto out;
+
+ if (memslot->is_dirty)
+ clear_user(memslot->dirty_bitmap, n);
+ } else {
+ unsigned long __user *dirty_bitmap;
+
+ dirty_bitmap = memslot->dirty_bitmap;
+ clear_user(memslot->dirty_bitmap_old, n);
+ memslot->dirty_bitmap = memslot->dirty_bitmap_old;
+ memslot->dirty_bitmap_old = dirty_bitmap;
+ log->addr = (unsigned long)dirty_bitmap;
+ }

r = 0;
out:
@@ -1699,6 +1714,21 @@ static long kvm_vm_ioctl(struct file *filp,
goto out;
break;
}
+ case KVM_SWITCH_DIRTY_LOG: {
+ struct kvm_dirty_log log;
+
+ r = -EFAULT;
+ if (copy_from_user(&log, argp, sizeof log))
+ goto out;
+ r = kvm_vm_ioctl_switch_dirty_log(kvm, &log);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user(argp, &log, sizeof log))
+ goto out;
+ r = 0;
+ break;
+ }
#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
case KVM_REGISTER_COALESCED_MMIO: {
struct kvm_coalesced_mmio_zone zone;
@@ -1790,7 +1820,7 @@ static long kvm_vm_compat_ioctl(struct file *filp,
goto out;
log.slot = compat_log.slot;
log.padding1 = compat_log.padding1;
- log.padding2 = compat_log.padding2;
+ log.addr = compat_log.padding2;
log.dirty_bitmap = compat_ptr(compat_log.dirty_bitmap);

r = kvm_vm_ioctl_get_dirty_log(kvm, &log);
@@ -1879,6 +1909,7 @@ static long kvm_dev_ioctl_check_extension_generic(long arg)
case KVM_CAP_SET_BOOT_CPU_ID:
#endif
case KVM_CAP_INTERNAL_ERROR_DATA:
+ case KVM_CAP_USER_DIRTY_BITMAP:
return 1;
#ifdef CONFIG_HAVE_KVM_IRQCHIP
case KVM_CAP_IRQ_ROUTING:
--
1.6.3.3