[kvm-devel] [PATCH 6/8] x86: KVM guest: hypercall batching
From: Marcelo Tosatti <[EMAIL PROTECTED]> Batch pte updates and tlb flushes in lazy MMU mode. v1->v2: - report individual hypercall error code, have multicall return number of processed entries. - cover entire multicall duration with slots_lock instead of acquiring/reacquiring. v2->v3: - change to one ioctl per paravirt feature v3->v4: - adjust to mmu_op - helper for getting para_state without debug warnings Signed-off-by: Marcelo Tosatti <[EMAIL PROTECTED]> Signed-off-by: Avi Kivity <[EMAIL PROTECTED]> --- arch/x86/kernel/kvm.c | 62 +++- 1 files changed, 60 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index e28d818..8405984 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -25,6 +25,22 @@ #include #include #include +#include + +#define MMU_QUEUE_SIZE 1024 + +struct kvm_para_state { + u8 mmu_queue[MMU_QUEUE_SIZE]; + int mmu_queue_len; + enum paravirt_lazy_mode mode; +}; + +static DEFINE_PER_CPU(struct kvm_para_state, para_state); + +static struct kvm_para_state *kvm_para_state(void) +{ + return &per_cpu(para_state, raw_smp_processor_id()); +} /* * No need for any "IO delay" on KVM @@ -47,6 +63,28 @@ static void kvm_mmu_op(void *buffer, unsigned len) } while (len); } +static void mmu_queue_flush(struct kvm_para_state *state) +{ + if (state->mmu_queue_len) { + kvm_mmu_op(state->mmu_queue, state->mmu_queue_len); + state->mmu_queue_len = 0; + } +} + +static void kvm_deferred_mmu_op(void *buffer, int len) +{ + struct kvm_para_state *state = kvm_para_state(); + + if (state->mode != PARAVIRT_LAZY_MMU) { + kvm_mmu_op(buffer, len); + return; + } + if (state->mmu_queue_len + len > sizeof state->mmu_queue) + mmu_queue_flush(state); + memcpy(state->mmu_queue + state->mmu_queue_len, buffer, len); + state->mmu_queue_len += len; +} + static void kvm_mmu_write(void *dest, u64 val) { struct kvm_mmu_op_write_pte wpte = { @@ -55,7 +93,7 @@ static void kvm_mmu_write(void *dest, u64 val) .pte_val = val, }; - kvm_mmu_op(&wpte, sizeof wpte); + kvm_deferred_mmu_op(&wpte, sizeof wpte); } /* @@ -122,7 +160,7 @@ static void kvm_flush_tlb(void) .header.op = KVM_MMU_OP_FLUSH_TLB, }; - kvm_mmu_op(&ftlb, sizeof ftlb); + kvm_deferred_mmu_op(&ftlb, sizeof ftlb); } static void kvm_release_pt(u32 pfn) @@ -135,6 +173,23 @@ static void kvm_release_pt(u32 pfn) kvm_mmu_op(&rpt, sizeof rpt); } +static void kvm_enter_lazy_mmu(void) +{ + struct kvm_para_state *state = kvm_para_state(); + + paravirt_enter_lazy_mmu(); + state->mode = paravirt_get_lazy_mode(); +} + +static void kvm_leave_lazy_mmu(void) +{ + struct kvm_para_state *state = kvm_para_state(); + + mmu_queue_flush(state); + paravirt_leave_lazy(paravirt_get_lazy_mode()); + state->mode = paravirt_get_lazy_mode(); +} + static void paravirt_ops_setup(void) { pv_info.name = "KVM"; @@ -160,6 +215,9 @@ static void paravirt_ops_setup(void) pv_mmu_ops.flush_tlb_user = kvm_flush_tlb; pv_mmu_ops.release_pt = kvm_release_pt; pv_mmu_ops.release_pd = kvm_release_pt; + + pv_mmu_ops.lazy_mode.enter = kvm_enter_lazy_mmu; + pv_mmu_ops.lazy_mode.leave = kvm_leave_lazy_mmu; } } -- 1.5.4.2 - This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse012070mrt/direct/01/ ___ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel
Re: [kvm-devel] [PATCH 6/8] x86: KVM guest: hypercall batching
Hi Avi, After reading the patch, I think the hypercall batching mechanism is as follows: 1 defer the MMU-related operations and buffer them in kvm_para_state->mmu_queue[] 2 during the flush period, kvm_mmu_op() is called to flush operations in kvm_para_state->mmu_queue[] 3 kvm_mmu_op() generate a hypercall for each operation in kvm_para_state->mmu_queue[]; thus trigger a context switch from guest mode to kernel mode for each operation. My question is: Is it possible to only generate a single hypercall(thus a single context switch) for all buffered MMU operations in kvm_para_state->mmu_queue[]? This way we could further reduce overhead, am I right? BTW. I don't have a deep understanding of KVM. So this is just a question out of my curiosity. Thanks, Forrest On 3/3/08, Avi Kivity <[EMAIL PROTECTED]> wrote: > From: Marcelo Tosatti <[EMAIL PROTECTED]> > > Batch pte updates and tlb flushes in lazy MMU mode. > > v1->v2: > - report individual hypercall error code, have multicall return number of > processed entries. > - cover entire multicall duration with slots_lock instead of > acquiring/reacquiring. > > v2->v3: > - change to one ioctl per paravirt feature > > v3->v4: > - adjust to mmu_op > - helper for getting para_state without debug warnings > > Signed-off-by: Marcelo Tosatti <[EMAIL PROTECTED]> > Signed-off-by: Avi Kivity <[EMAIL PROTECTED]> > --- > arch/x86/kernel/kvm.c | 62 > +++- > 1 files changed, 60 insertions(+), 2 deletions(-) > > diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c > index e28d818..8405984 100644 > --- a/arch/x86/kernel/kvm.c > +++ b/arch/x86/kernel/kvm.c > @@ -25,6 +25,22 @@ > #include > #include > #include > +#include > + > +#define MMU_QUEUE_SIZE 1024 > + > +struct kvm_para_state { > + u8 mmu_queue[MMU_QUEUE_SIZE]; > + int mmu_queue_len; > + enum paravirt_lazy_mode mode; > +}; > + > +static DEFINE_PER_CPU(struct kvm_para_state, para_state); > + > +static struct kvm_para_state *kvm_para_state(void) > +{ > + return &per_cpu(para_state, raw_smp_processor_id()); > +} > > /* > * No need for any "IO delay" on KVM > @@ -47,6 +63,28 @@ static void kvm_mmu_op(void *buffer, unsigned len) > } while (len); > } > > +static void mmu_queue_flush(struct kvm_para_state *state) > +{ > + if (state->mmu_queue_len) { > + kvm_mmu_op(state->mmu_queue, state->mmu_queue_len); > + state->mmu_queue_len = 0; > + } > +} > + > +static void kvm_deferred_mmu_op(void *buffer, int len) > +{ > + struct kvm_para_state *state = kvm_para_state(); > + > + if (state->mode != PARAVIRT_LAZY_MMU) { > + kvm_mmu_op(buffer, len); > + return; > + } > + if (state->mmu_queue_len + len > sizeof state->mmu_queue) > + mmu_queue_flush(state); > + memcpy(state->mmu_queue + state->mmu_queue_len, buffer, len); > + state->mmu_queue_len += len; > +} > + > static void kvm_mmu_write(void *dest, u64 val) > { > struct kvm_mmu_op_write_pte wpte = { > @@ -55,7 +93,7 @@ static void kvm_mmu_write(void *dest, u64 val) > .pte_val = val, > }; > > - kvm_mmu_op(&wpte, sizeof wpte); > + kvm_deferred_mmu_op(&wpte, sizeof wpte); > } > > /* > @@ -122,7 +160,7 @@ static void kvm_flush_tlb(void) > .header.op = KVM_MMU_OP_FLUSH_TLB, > }; > > - kvm_mmu_op(&ftlb, sizeof ftlb); > + kvm_deferred_mmu_op(&ftlb, sizeof ftlb); > } > > static void kvm_release_pt(u32 pfn) > @@ -135,6 +173,23 @@ static void kvm_release_pt(u32 pfn) > kvm_mmu_op(&rpt, sizeof rpt); > } > > +static void kvm_enter_lazy_mmu(void) > +{ > + struct kvm_para_state *state = kvm_para_state(); > + > + paravirt_enter_lazy_mmu(); > + state->mode = paravirt_get_lazy_mode(); > +} > + > +static void kvm_leave_lazy_mmu(void) > +{ > + struct kvm_para_state *state = kvm_para_state(); > + > + mmu_queue_flush(state); > + paravirt_leave_lazy(paravirt_get_lazy_mode()); > + state->mode = paravirt_get_lazy_mode(); > +} > + > static void paravirt_ops_setup(void) > { > pv_info.name = "KVM"; > @@ -160,6 +215,9 @@ static void paravirt_ops_setup(void) > pv_mmu_ops.flush_tlb_user = kvm_flush_tlb; > pv_mmu_ops.release_pt = kvm_release_pt; > pv_mmu_ops.release_pd = kvm_release_pt; > + > + pv_mmu_ops.lazy_mode.enter = kvm_enter_lazy_mmu; > + pv_mmu_ops.lazy_mode.leave = kvm_leave_lazy_mmu; > } > } > > -- > 1.5.4.2 > > > - > This SF.net email is sponsored by: Microsoft > Defy all challenges. Microsoft(R) Visual Studio 2008. > http://clk.atdmt.com/MRT/go/vse012070mrt/direct/01/ > ___ > kvm-devel mailing list > kvm-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/kvm-devel > - This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse012070mrt/direct/01/ ___
Re: [kvm-devel] [PATCH 6/8] x86: KVM guest: hypercall batching
Zhao Forrest wrote: > Hi Avi, > > After reading the patch, I think the hypercall batching mechanism is as > follows: > 1 defer the MMU-related operations and buffer them in > kvm_para_state->mmu_queue[] > 2 during the flush period, kvm_mmu_op() is called to flush operations > in kvm_para_state->mmu_queue[] > 3 kvm_mmu_op() generate a hypercall for each operation in > kvm_para_state->mmu_queue[]; thus trigger a context switch from guest > mode to kernel mode for each operation. > > My question is: Is it possible to only generate a single > hypercall(thus a single context switch) for all buffered MMU > operations in kvm_para_state->mmu_queue[]? This way we could further > reduce overhead, am I right? > BTW. I don't have a deep understanding of KVM. So this is just a > question out of my curiosity. > > mmu_queue_flush() is called once per batch, so we only have one hypercall per batch (at least if the data doesn't exceed 512 bytes). -- Do not meddle in the internals of kernels, for they are subtle and quick to panic. - This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse012070mrt/direct/01/ ___ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel