[PATCH 10/25] KVM: emulate: introduce memory_prepare callback to speed up memory access

From: Paolo Bonzini
Date: Mon Jun 09 2014 - 08:59:41 EST


Emulating a RMW instruction currently walks the page tables
twice. To avoid this, store the virtual address in the host
and access it directly.

In fact, it turns out that the optimizations we can do
actually benefit all memory accesses.

Reviewed-by: Marcelo Tosatti <mtosatti@xxxxxxxxxx>
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
arch/x86/include/asm/kvm_emulate.h | 26 +++++++++++++++
arch/x86/kvm/x86.c | 67 ++++++++++++++++++++++++++++++++++++++
include/linux/kvm_host.h | 5 +++
virt/kvm/kvm_main.c | 5 ---
4 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index 46725e8aa8cb..1aa2adf0bb1a 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -167,6 +167,32 @@ struct x86_emulate_ops {
const void *new,
unsigned int bytes,
struct x86_exception *fault);
+
+ /*
+ * memory_prepare: Prepare userspace access fastpath.
+ * @addr: [IN ] Linear address to access.
+ * @bytes: [IN ] Number of bytes to access.
+ * @write: [IN ] True if *p_hva will be written to.
+ * @p_opaque: [OUT] Value passed back to memory_finish.
+ * @p_hva: [OUT] Host virtual address for __copy_from/to_user.
+ */
+ int (*memory_prepare)(struct x86_emulate_ctxt *ctxt,
+ unsigned long addr,
+ unsigned int bytes,
+ struct x86_exception *exception,
+ bool write,
+ void **p_opaque,
+ unsigned long *p_hva);
+
+ /*
+ * memory_finish: Complete userspace access fastpath.
+ * @opaque: [OUT] Value passed back from memory_prepare.
+ * @hva: [OUT] Host virtual address computed in memory_prepare.
+ */
+ void (*memory_finish)(struct x86_emulate_ctxt *ctxt,
+ void *p_opaque,
+ unsigned long p_hva);
+
void (*invlpg)(struct x86_emulate_ctxt *ctxt, ulong addr);

int (*pio_in_emulated)(struct x86_emulate_ctxt *ctxt,
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e64b8b5cb6cd..02678c2f3721 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4279,6 +4279,71 @@ static const struct read_write_emulator_ops write_emultor = {
.write = true,
};

+static int emulator_memory_prepare(struct x86_emulate_ctxt *ctxt,
+ unsigned long addr,
+ unsigned int bytes,
+ struct x86_exception *exception,
+ bool write,
+ void **p_opaque,
+ unsigned long *p_hva)
+{
+ struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
+ struct kvm_memory_slot *memslot;
+ int ret;
+ gpa_t gpa;
+ gfn_t gfn;
+ unsigned long hva;
+
+ if (unlikely(((addr + bytes - 1) ^ addr) & PAGE_MASK))
+ goto no_hva;
+
+ ret = vcpu_mmio_gva_to_gpa(vcpu, addr, &gpa, exception, true);
+ if (ret != 0) {
+ if (ret < 0)
+ return X86EMUL_PROPAGATE_FAULT;
+ goto no_hva;
+ }
+
+ /* A (heavily) simplified version of kvm_gfn_to_hva_cache_init. */
+ gfn = gpa >> PAGE_SHIFT;
+ memslot = gfn_to_memslot(vcpu->kvm, gfn);
+ if (!memslot)
+ goto no_hva;
+
+ if (write) {
+ if (memslot_is_readonly(memslot))
+ goto no_hva;
+
+ *p_opaque = memslot->dirty_bitmap ? memslot : NULL;
+ }
+
+ hva = __gfn_to_hva_memslot(memslot, gfn);
+ if (kvm_is_error_hva(hva))
+ goto no_hva;
+
+ *p_hva = hva + offset_in_page(gpa);
+ return X86EMUL_CONTINUE;
+
+no_hva:
+ *p_hva = KVM_HVA_ERR_BAD;
+ return X86EMUL_CONTINUE;
+}
+
+static void emulator_memory_finish(struct x86_emulate_ctxt *ctxt,
+ void *opaque,
+ unsigned long hva)
+{
+ struct kvm_memory_slot *memslot;
+ gfn_t gfn;
+
+ if (!opaque)
+ return;
+
+ memslot = opaque;
+ gfn = hva_to_gfn_memslot(hva, memslot);
+ mark_page_dirty_in_slot(memslot, gfn);
+}
+
static int emulator_read_write_onepage(unsigned long addr, void *val,
unsigned int bytes,
struct x86_exception *exception,
@@ -4823,6 +4888,8 @@ static const struct x86_emulate_ops emulate_ops = {
.read_std = kvm_read_guest_virt_system,
.write_std = kvm_write_guest_virt_system,
.fetch = kvm_fetch_guest_virt,
+ .memory_prepare = emulator_memory_prepare,
+ .memory_finish = emulator_memory_finish,
.read_emulated = emulator_read_emulated,
.write_emulated = emulator_write_emulated,
.cmpxchg_emulated = emulator_cmpxchg_emulated,
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5be9805b0aeb..91f303dd0fe5 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -842,6 +842,11 @@ search_memslots(struct kvm_memslots *slots, gfn_t gfn)
return NULL;
}

+static inline bool memslot_is_readonly(struct kvm_memory_slot *slot)
+{
+ return slot->flags & KVM_MEM_READONLY;
+}
+
static inline struct kvm_memory_slot *
__gfn_to_memslot(struct kvm_memslots *slots, gfn_t gfn)
{
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2f9bc20ae2a7..09b19afb2c11 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1028,11 +1028,6 @@ out:
return size;
}

-static bool memslot_is_readonly(struct kvm_memory_slot *slot)
-{
- return slot->flags & KVM_MEM_READONLY;
-}
-
static unsigned long __gfn_to_hva_many(struct kvm_memory_slot *slot, gfn_t gfn,
gfn_t *nr_pages, bool write)
{
--
1.8.3.1


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/