This patchset is splited from my previous patchset:
[PATCH v3 00/15] KVM: MMU: locklessly write-protect
that can be found at:
https://lkml.org/lkml/2013/10/23/265
Changelog v4 :
- add more comments for patch 5 and thank for Marcelo's review
Xiao Guangrong (5):
Revert "KVM: Simplify kvm->tlbs_dirty handling"
KVM: MMU: properly check last spte in fast_page_fault()
KVM: MMU: lazily drop large spte
KVM: MMU: flush tlb if the spte can be locklessly modified
KVM: MMU: flush tlb out of mmu lock when write-protect the sptes
arch/x86/kvm/mmu.c | 72 ++++++++++++++++++++++++++++++----------------
arch/x86/kvm/mmu.h | 14 +++++++++
arch/x86/kvm/paging_tmpl.h | 7 ++---
arch/x86/kvm/x86.c | 20 ++++++++++---
include/linux/kvm_host.h | 4 +--
virt/kvm/kvm_main.c | 5 +++-
6 files changed, 85 insertions(+), 37 deletions(-)
--
1.8.1.4
Relax the tlb flush condition since we will write-protect the spte out of mmu
lock. Note lockless write-protection only marks the writable spte to readonly
and the spte can be writable only if both SPTE_HOST_WRITEABLE and
SPTE_MMU_WRITEABLE are set (that are tested by spte_is_locklessly_modifiable)
This patch is used to avoid this kind of race:
VCPU 0 VCPU 1
lockless wirte protection:
set spte.w = 0
lock mmu-lock
write protection the spte to sync shadow page,
see spte.w = 0, then without flush tlb
unlock mmu-lock
!!! At this point, the shadow page can still be
writable due to the corrupt tlb entry
Flush all TLB
Reviewed-by: Marcelo Tosatti <redacted>
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/mmu.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Now we can flush all the TLBs out of the mmu lock without TLB corruption when
write-proect the sptes, it is because:
- we have marked large sptes readonly instead of dropping them that means we
just change the spte from writable to readonly so that we only need to care
the case of changing spte from present to present (changing the spte from
present to nonpresent will flush all the TLBs immediately), in other words,
the only case we need to care is mmu_spte_update()
- in mmu_spte_update(), we haved checked
SPTE_HOST_WRITEABLE | PTE_MMU_WRITEABLE instead of PT_WRITABLE_MASK, that
means it does not depend on PT_WRITABLE_MASK anymore
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/mmu.c | 25 +++++++++++++++++++++----
arch/x86/kvm/mmu.h | 14 ++++++++++++++
arch/x86/kvm/x86.c | 12 ++++++++++--
3 files changed, 45 insertions(+), 6 deletions(-)
@@ -96,6 +96,20 @@ static inline int is_present_gpte(unsigned long pte)returnpte&PT_PRESENT_MASK;}+/*+*PleasenotePT_WRITABLE_MASKisnotstablesince+*1)fast_page_fault()setssptefromreadonlytowritableoutofmmu-lockor+*2)kvm_mmu_slot_remove_write_access()andkvm_vm_ioctl_get_dirty_log()+*canwriteprotectsptesbutflushtlboutmmu-lockthatmeanswemayuse+*thecorrupttlbentrieswhichdependonthisbit.+*+*Bothcasesdonotmodifythestatusofspte_is_locklessly_modifiable()so+*ifyouwanttocheckwhetherthespteiswritableonMMUyoucancheck+*SPTE_MMU_WRITEABLEinstead.Ifyouwanttoupdatesptewithoutlosing+*A/Dbitsandtlbflush,youcancheckspte_is_locklessly_modifiable()+*instead.Seethecommentsinspte_has_volatile_bits()and+*mmu_spte_update().+*/staticinlineintis_writable_pte(unsignedlongpte){returnpte&PT_WRITABLE_MASK;
@@ -3642,11 +3642,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)offset=i*BITS_PER_LONG;kvm_mmu_write_protect_pt_masked(kvm,memslot,offset,mask);}-if(is_dirty)-kvm_flush_remote_tlbs(kvm);spin_unlock(&kvm->mmu_lock);+/* See the comments in kvm_mmu_slot_remove_write_access(). */+lockdep_assert_held(&kvm->slots_lock);++/*+*AlltheTLBscanbeflushedoutofmmulock,seethecommentsin+*kvm_mmu_slot_remove_write_access().+*/+if(is_dirty)+kvm_flush_remote_tlbs(kvm);+r=-EFAULT;if(copy_to_user(log->dirty_bitmap,dirty_bitmap_buffer,n))gotoout;
Currently, kvm zaps the large spte if write-protected is needed, the later
read can fault on that spte. Actually, we can make the large spte readonly
instead of making them un-present, the page fault caused by read access can
be avoided
The idea is from Avi:
| As I mentioned before, write-protecting a large spte is a good idea,
| since it moves some work from protect-time to fault-time, so it reduces
| jitter. This removes the need for the return value.
This version has fixed the issue reported in 6b73a9606, the reason of that
issue is that fast_page_fault() directly sets the readonly large spte to
writable but only dirty the first page into the dirty-bitmap that means
other pages are missed. Fixed it by only the normal sptes (on the
PT_PAGE_TABLE_LEVEL level) can be fast fixed
Reviewed-by: Marcelo Tosatti <redacted>
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/mmu.c | 34 ++++++++++++++++++----------------
arch/x86/kvm/x86.c | 8 ++++++--
2 files changed, 24 insertions(+), 18 deletions(-)
This reverts commit 5befdc385ddb2d5ae8995ad89004529a3acf58fc.
Since we will allow flush tlb out of mmu-lock in the later
patch
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/paging_tmpl.h | 7 +++----
include/linux/kvm_host.h | 4 +---
virt/kvm/kvm_main.c | 5 ++++-
3 files changed, 8 insertions(+), 8 deletions(-)
Using sp->role.level instead of @level since @level is not got from the
page table hierarchy
There is no issue in current code since the fast page fault currently only
fixes the fault caused by dirty-log that is always on the last level
(level = 1)
This patch makes the code more readable and avoids potential issue in the
further development
Reviewed-by: Marcelo Tosatti <redacted>
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/mmu.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
Ping......
On 03/10/2014 10:01 PM, Xiao Guangrong wrote:
This patchset is splited from my previous patchset:
[PATCH v3 00/15] KVM: MMU: locklessly write-protect
that can be found at:
https://lkml.org/lkml/2013/10/23/265
Changelog v4 :
- add more comments for patch 5 and thank for Marcelo's review
Xiao Guangrong (5):
Revert "KVM: Simplify kvm->tlbs_dirty handling"
KVM: MMU: properly check last spte in fast_page_fault()
KVM: MMU: lazily drop large spte
KVM: MMU: flush tlb if the spte can be locklessly modified
KVM: MMU: flush tlb out of mmu lock when write-protect the sptes
arch/x86/kvm/mmu.c | 72 ++++++++++++++++++++++++++++++----------------
arch/x86/kvm/mmu.h | 14 +++++++++
arch/x86/kvm/paging_tmpl.h | 7 ++---
arch/x86/kvm/x86.c | 20 ++++++++++---
include/linux/kvm_host.h | 4 +--
virt/kvm/kvm_main.c | 5 +++-
6 files changed, 85 insertions(+), 37 deletions(-)
From: Paolo Bonzini <pbonzini@redhat.com> Date: 2014-03-25 10:22:23
Il 25/03/2014 04:29, Xiao Guangrong ha scritto:
Ping......
Hi Xiao, I'll look at these patches soon. I plan to merge them for 3.16.
Paolo
On 03/10/2014 10:01 PM, Xiao Guangrong wrote:
quoted
This patchset is splited from my previous patchset:
[PATCH v3 00/15] KVM: MMU: locklessly write-protect
that can be found at:
https://lkml.org/lkml/2013/10/23/265
Changelog v4 :
- add more comments for patch 5 and thank for Marcelo's review
Xiao Guangrong (5):
Revert "KVM: Simplify kvm->tlbs_dirty handling"
KVM: MMU: properly check last spte in fast_page_fault()
KVM: MMU: lazily drop large spte
KVM: MMU: flush tlb if the spte can be locklessly modified
KVM: MMU: flush tlb out of mmu lock when write-protect the sptes
arch/x86/kvm/mmu.c | 72 ++++++++++++++++++++++++++++++----------------
arch/x86/kvm/mmu.h | 14 +++++++++
arch/x86/kvm/paging_tmpl.h | 7 ++---
arch/x86/kvm/x86.c | 20 ++++++++++---
include/linux/kvm_host.h | 4 +--
virt/kvm/kvm_main.c | 5 +++-
6 files changed, 85 insertions(+), 37 deletions(-)
From: Hu Yaohui <hidden> Date: 2014-03-25 16:25:09
Hi Guangrong,
Since you have written in the kvm/mmu.txt.
<quote>
unsync:
If true, then the translations in this page may not match the guest's
translation. This is equivalent to the state of the tlb when a pte is
changed but before the tlb entry is flushed. Accordingly, unsync ptes
are synchronized when the guest executes invlpg or flushes its tlb by
other means. Valid for leaf pages.
</quote>
This make sense to me, my question is when those unsync bits will be
set? When the guest writes to the level 1 guest page tables, it will
not cause a page fault. Those unsync bit is unlikely to be set when
the entry is modified. (correct me if I am wrong).
<code>
static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
{
...
for_each_shadow_entry(vcpu, gva, iterator) {
level = iterator.level;
sptep = iterator.sptep;
sp = page_header(__pa(sptep));
if (is_last_spte(*sptep, level)) {
int offset, shift;
if (!sp->unsync)
break;
...
</code>
When guest called invlpg, kvm invlpg will navigate to to the last
level, if the sp->unsync is not set to 1, since the initial value is
zero. it will just break. It's not straight forward to me that the
specified sp will be synced with the guest page table.
I think I have missed something or misunderstood the whole mechanism,
I would really appreciate it if you could shed some lights on that.
Best Wishes,
Yaohui Hu
On Mon, Mar 10, 2014 at 10:01 AM, Xiao Guangrong
[off-list ref] wrote:
This patchset is splited from my previous patchset:
[PATCH v3 00/15] KVM: MMU: locklessly write-protect
that can be found at:
https://lkml.org/lkml/2013/10/23/265
Changelog v4 :
- add more comments for patch 5 and thank for Marcelo's review
Xiao Guangrong (5):
Revert "KVM: Simplify kvm->tlbs_dirty handling"
KVM: MMU: properly check last spte in fast_page_fault()
KVM: MMU: lazily drop large spte
KVM: MMU: flush tlb if the spte can be locklessly modified
KVM: MMU: flush tlb out of mmu lock when write-protect the sptes
arch/x86/kvm/mmu.c | 72 ++++++++++++++++++++++++++++++----------------
arch/x86/kvm/mmu.h | 14 +++++++++
arch/x86/kvm/paging_tmpl.h | 7 ++---
arch/x86/kvm/x86.c | 20 ++++++++++---
include/linux/kvm_host.h | 4 +--
virt/kvm/kvm_main.c | 5 +++-
6 files changed, 85 insertions(+), 37 deletions(-)
--
1.8.1.4
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Hu Yaohui <hidden> Date: 2014-03-26 04:40:09
Hi all,
I hope you have a good day!
I have debugged the code myself. I have called dump_stack() in
function "__kvm_unsync_page"
and function "invlpg". Actually every time before invlpg is called,
the page fault handled will call "__kvm_unsync_page" before invlpg to
mark the specified sp as unsynced. (correct me if I am wrong). I am
wondering why there is a page fault. AFAIK when calling flush_tlb_page
in the guest os. it will issue invlpg instruction directly, I did not
see any operation which could always cause the page fault.I would
really appreciate if if someone could shed me some lights on it.
Thanks for your time!
Best Wishes,
Yaohui
On Tue, Mar 25, 2014 at 12:25 PM, Hu Yaohui [off-list ref] wrote:
Hi Guangrong,
Since you have written in the kvm/mmu.txt.
<quote>
unsync:
If true, then the translations in this page may not match the guest's
translation. This is equivalent to the state of the tlb when a pte is
changed but before the tlb entry is flushed. Accordingly, unsync ptes
are synchronized when the guest executes invlpg or flushes its tlb by
other means. Valid for leaf pages.
</quote>
This make sense to me, my question is when those unsync bits will be
set? When the guest writes to the level 1 guest page tables, it will
not cause a page fault. Those unsync bit is unlikely to be set when
the entry is modified. (correct me if I am wrong).
<code>
static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
{
...
for_each_shadow_entry(vcpu, gva, iterator) {
level = iterator.level;
sptep = iterator.sptep;
sp = page_header(__pa(sptep));
if (is_last_spte(*sptep, level)) {
int offset, shift;
if (!sp->unsync)
break;
...
</code>
When guest called invlpg, kvm invlpg will navigate to to the last
level, if the sp->unsync is not set to 1, since the initial value is
zero. it will just break. It's not straight forward to me that the
specified sp will be synced with the guest page table.
I think I have missed something or misunderstood the whole mechanism,
I would really appreciate it if you could shed some lights on that.
Best Wishes,
Yaohui Hu
On Mon, Mar 10, 2014 at 10:01 AM, Xiao Guangrong
[off-list ref] wrote:
quoted
This patchset is splited from my previous patchset:
[PATCH v3 00/15] KVM: MMU: locklessly write-protect
that can be found at:
https://lkml.org/lkml/2013/10/23/265
Changelog v4 :
- add more comments for patch 5 and thank for Marcelo's review
Xiao Guangrong (5):
Revert "KVM: Simplify kvm->tlbs_dirty handling"
KVM: MMU: properly check last spte in fast_page_fault()
KVM: MMU: lazily drop large spte
KVM: MMU: flush tlb if the spte can be locklessly modified
KVM: MMU: flush tlb out of mmu lock when write-protect the sptes
arch/x86/kvm/mmu.c | 72 ++++++++++++++++++++++++++++++----------------
arch/x86/kvm/mmu.h | 14 +++++++++
arch/x86/kvm/paging_tmpl.h | 7 ++---
arch/x86/kvm/x86.c | 20 ++++++++++---
include/linux/kvm_host.h | 4 +--
virt/kvm/kvm_main.c | 5 +++-
6 files changed, 85 insertions(+), 37 deletions(-)
--
1.8.1.4
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
A suggestion: please send a new mail to ask question, especially,
when your question is not related to the patches, so that others
will probably discover the topic and join the discussion.
On 03/26/2014 12:25 AM, Hu Yaohui wrote:
Hi Guangrong,
Since you have written in the kvm/mmu.txt.
<quote>
unsync:
If true, then the translations in this page may not match the guest's
translation. This is equivalent to the state of the tlb when a pte is
changed but before the tlb entry is flushed. Accordingly, unsync ptes
are synchronized when the guest executes invlpg or flushes its tlb by
other means. Valid for leaf pages.
</quote>
This make sense to me, my question is when those unsync bits will be
set? When the guest writes to the level 1 guest page tables, it will
not cause a page fault. Those unsync bit is unlikely to be set when
the entry is modified. (correct me if I am wrong).
The bit is set in mmu_need_write_protect() where the host makes decision
if the page need to be write-protected (!unsync) or to be unsynced.
Hi all,
I hope you have a good day!
I have debugged the code myself. I have called dump_stack() in
function "__kvm_unsync_page"
and function "invlpg". Actually every time before invlpg is called,
the page fault handled will call "__kvm_unsync_page" before invlpg to
mark the specified sp as unsynced. (correct me if I am wrong). I am
wondering why there is a page fault. AFAIK when calling flush_tlb_page
in the guest os. it will issue invlpg instruction directly, I did not
see any operation which could always cause the page fault.I would
really appreciate if if someone could shed me some lights on it.
Thanks for your time!
Page fault is used to map a page into guest and set the proper permission
on for it so this is the right place to make decision if the page need be
writable.
Tlb flush is used when guest wants to have a clean tlb so that host syncs the
unsync page tables when it is happing.
From: Hu Yaohui <hidden> Date: 2014-03-26 05:30:51
Hi Guangrong,
Thanks a lot!
I will reopen a new discussion next time.
Does that means once the level 1 guest page table entry is modified,
the host should make a decision in page fault handler whether the page
sync or unsync?
My question is that since all level 1 guest page tables are writable,
when the entry in the page table is modified. there is no page fault
will happen, how can the mmu_need_write_protect to be called to make
whether sync or unsync decision?
Thanks for your time!
Best Wishes,
Yaohui
On Wed, Mar 26, 2014 at 1:07 AM, Xiao Guangrong
[off-list ref] wrote:
On 03/26/2014 12:40 PM, Hu Yaohui wrote:
quoted
Hi all,
I hope you have a good day!
I have debugged the code myself. I have called dump_stack() in
function "__kvm_unsync_page"
and function "invlpg". Actually every time before invlpg is called,
the page fault handled will call "__kvm_unsync_page" before invlpg to
mark the specified sp as unsynced. (correct me if I am wrong). I am
wondering why there is a page fault. AFAIK when calling flush_tlb_page
in the guest os. it will issue invlpg instruction directly, I did not
see any operation which could always cause the page fault.I would
really appreciate if if someone could shed me some lights on it.
Thanks for your time!
Page fault is used to map a page into guest and set the proper permission
on for it so this is the right place to make decision if the page need be
writable.
Tlb flush is used when guest wants to have a clean tlb so that host syncs the
unsync page tables when it is happing.
On Mon, Mar 10, 2014 at 10:01:49PM +0800, Xiao Guangrong wrote:
quoted hunk
Now we can flush all the TLBs out of the mmu lock without TLB corruption when
write-proect the sptes, it is because:
- we have marked large sptes readonly instead of dropping them that means we
just change the spte from writable to readonly so that we only need to care
the case of changing spte from present to present (changing the spte from
present to nonpresent will flush all the TLBs immediately), in other words,
the only case we need to care is mmu_spte_update()
- in mmu_spte_update(), we haved checked
SPTE_HOST_WRITEABLE | PTE_MMU_WRITEABLE instead of PT_WRITABLE_MASK, that
means it does not depend on PT_WRITABLE_MASK anymore
Signed-off-by: Xiao Guangrong <redacted>
---
arch/x86/kvm/mmu.c | 25 +++++++++++++++++++++----
arch/x86/kvm/mmu.h | 14 ++++++++++++++
arch/x86/kvm/x86.c | 12 ++++++++++--
3 files changed, 45 insertions(+), 6 deletions(-)
@@ -96,6 +96,20 @@ static inline int is_present_gpte(unsigned long pte)returnpte&PT_PRESENT_MASK;}+/*+*PleasenotePT_WRITABLE_MASKisnotstablesince+*1)fast_page_fault()setssptefromreadonlytowritableoutofmmu-lockor+*2)kvm_mmu_slot_remove_write_access()andkvm_vm_ioctl_get_dirty_log()+*canwriteprotectsptesbutflushtlboutmmu-lockthatmeanswemayuse+*thecorrupttlbentrieswhichdependonthisbit.+*+*Bothcasesdonotmodifythestatusofspte_is_locklessly_modifiable()so+*ifyouwanttocheckwhetherthespteiswritableonMMUyoucancheck+*SPTE_MMU_WRITEABLEinstead.Ifyouwanttoupdatesptewithoutlosing+*A/Dbitsandtlbflush,youcancheckspte_is_locklessly_modifiable()+*instead.Seethecommentsinspte_has_volatile_bits()and+*mmu_spte_update().+*/staticinlineintis_writable_pte(unsignedlongpte){
Xiao,
Can't get the SPTE_MMU_WRITEABLE part.
So assume you are writing code to perform some action after guest memory
has been write protected. You would
spin_lock(mmu_lock);
if (writeable spte bit is set)
remove writeable spte bit from spte
remote TLB flush (*)
action
spin_unlock(mmu_lock);
(*) is necessary because reading the writeable spte bit as zero
does not guarantee remote TLBs have been flushed.
Now what SPTE_MMU_WRITEABLE has to do with it ?
Perhaps a recipe like that (or just the rules) would be useful.
The remaining patches look good.
Hi Marcelo,
Thanks your time to review it.
On 04/09/2014 10:51 PM, Marcelo Tosatti wrote:
quoted
+/*+ * Please note PT_WRITABLE_MASK is not stable since+ * 1) fast_page_fault() sets spte from readonly to writable out of mmu-lock or+ * 2) kvm_mmu_slot_remove_write_access() and kvm_vm_ioctl_get_dirty_log()+ * can write protect sptes but flush tlb out mmu-lock that means we may use+ * the corrupt tlb entries which depend on this bit.+ *+ * Both cases do not modify the status of spte_is_locklessly_modifiable() so+ * if you want to check whether the spte is writable on MMU you can check+ * SPTE_MMU_WRITEABLE instead. If you want to update spte without losing+ * A/D bits and tlb flush, you can check spte_is_locklessly_modifiable()+ * instead. See the comments in spte_has_volatile_bits() and+ * mmu_spte_update().+ */ static inline int is_writable_pte(unsigned long pte) {
Xiao,
Can't get the SPTE_MMU_WRITEABLE part.
So assume you are writing code to perform some action after guest memory
has been write protected. You would
spin_lock(mmu_lock);
if (writeable spte bit is set)
remove writeable spte bit from spte
remote TLB flush (*)
action
spin_unlock(mmu_lock);
(*) is necessary because reading the writeable spte bit as zero
does not guarantee remote TLBs have been flushed.
Now what SPTE_MMU_WRITEABLE has to do with it ?
It is contained in "remove writeable spte bit from spte" which
is done by mmu_spte_update():
if (spte_is_locklessly_modifiable(old_spte) &&
!is_writable_pte(new_spte))
ret = true;
Perhaps a recipe like that (or just the rules) would be useful.
Okay, i am considering to improve this comments, how about like
this:
Currently, we have two sorts of write-protection, a) the first
one write-protects guest page to sync the guest modification,
b) another one is used to sync dirty bitmap when we do
KVM_GET_DIRTY_LOG. The differences between these two sorts are:
1) the first case clears SPTE_MMU_WRITEABLE bit.
2) the first case requires flushing tlb immediately avoiding
corrupting shadow page table between all vcpus so it should
be in the protection of mmu-lock. And the another case does
not need to flush tlb until returning the dirty bitmap to
userspace since it only write-protects the page logged in
the bitmap, that means the page in the dirty bitmap is not
missed, so it can flush tlb out of mmu-lock.
So, there is the problem: the first case can meet the corrupted
tlb caused by another case which write-protects pages but without
flush tlb immediately. In order to making the first case be aware
this problem we let it flush tlb if we try to write-protect
a spte whose SPTE_MMU_WRITEABLE bit is set, it works since another
case never touches SPTE_MMU_WRITEABLE bit.
Anyway, whenever a spte is updated (only permission and status bits
are changed) we need to check whether the spte with SPTE_MMU_WRITEABLE
becomes readonly, if that happens, we need to flush tlb. Fortunately,
mmu_spte_update() has already handled it perfectly.
The rules to use SPTE_MMU_WRITEABLE and PT_WRITABLE_MASK:
- if we want to see if it has writable tlb entry or if the spte can
be writable on the mmu mapping, check SPTE_MMU_WRITEABLE, this is
the most case, otherwise
- if we fix page fault on the spte or do write-protection by dirty logging,
check PT_WRITABLE_MASK.
TODO: introduce APIs to split these two cases.