kvm, x86: use ro page and don't copy shared page
PATCH 5/6 kvm, x86: use ro page and don't copy shared page
When page fault, we always call get_user_pages(write=1).
Actually, we don't need to do this when it is not write fault. get_user_pages(write=1) will cause shared page(ksm) copied. If this page is not modified in future, this copying and the copied page are just wasted. Ksm may scan and merge them and may cause thrash.
In this patch, if the page is RO for host VMM and it not write fault for guest, we will use RO page, otherwise we use a writable page.
---
arch/x86/kvm/mmu.c +48 -5
@@ -1832,6 +1832,45 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) } } +/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ; +return kvm_get_pfn_for_gfn (kvm, gfn); +} +static int mmu_need_write_protect (struct kvm_vcpu *vcpu,gfn_t gfn,bool can_unsync) {
@@ -2085,6 +2124,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)int level;pfn_t pfn;unsigned long mmu_seq; +int host_writable; level =mapping_level (vcpu, gfn);
@@ -2099,7 +2139,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, gfn, write, &host_writable);/* mmio */ if (is_error_pfn (pfn))
@@ -2109,7 +2149,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)if (mmu_notifier_retry (vcpu, mmu_seq)) goto out_unlock;kvm_mmu_free_some_pages (vcpu); - r =__direct_map (vcpu, v, write, level, gfn, pfn, true); + r =__direct_map (vcpu, v, write, level, gfn, pfn, host_writable);spin_unlock (&vcpu->kvm->mmu_lock);
@@ -2307,6 +2347,8 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,int level;gfn_t gfn = gpa >>PAGE_SHIFT ;unsigned long mmu_seq; +int write_fault = error_code &PFERR_WRITE_MASK ; +int host_writable;ASSERT (vcpu);ASSERT (VALID_PAGE (vcpu->arch.mmu.root_hpa));
@@ -2321,15 +2363,16 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, gfn, write_fault, + &host_writable);if (is_error_pfn (pfn))return kvm_handle_bad_page (vcpu->kvm, gfn, pfn);spin_lock (&vcpu->kvm->mmu_lock);if (mmu_notifier_retry (vcpu, mmu_seq)) goto out_unlock;kvm_mmu_free_some_pages (vcpu); - r =__direct_map (vcpu, gpa, error_code &PFERR_WRITE_MASK , - level, gfn, pfn, true); + r =__direct_map (vcpu, gpa, write_fault, + level, gfn, pfn, host_writable);spin_unlock (&vcpu->kvm->mmu_lock);return r;
arch/x86/kvm/paging_tmpl.h +4 -2
@@ -430,6 +430,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,pfn_t pfn;int level =PT_PAGE_TABLE_LEVEL ;unsigned long mmu_seq; +int host_writable;pgprintk ("%s: addr %lx err %x\n" , __func__, addr, error_code);kvm_mmu_audit (vcpu,"pre page fault" );
@@ -461,7 +462,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, walker.gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, walker.gfn, write_fault, + &host_writable);/* mmio */ if (is_error_pfn (pfn))
@@ -472,7 +474,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, goto out_unlock;kvm_mmu_free_some_pages (vcpu); sptep =FNAME (fetch)(vcpu, addr, &walker, user_fault, write_fault, - level, &write_pt, pfn, true); + level, &write_pt, pfn, host_writable); (void )sptep;pgprintk ("%s: shadow pte %p %llx ptwrite %d\n" , __func__, sptep, *sptep, write_pt);
arch/x86/mm/gup.c +2 -0
@@ -8,6 +8,7 @@#include <linux/mm.h> #include <linux/vmstat.h> #include <linux/highmem.h> +#include <linux/module.h> #include <asm/pgtable.h>
@@ -274,6 +275,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,return nr; } +EXPORT_SYMBOL_GPL (__get_user_pages_fast);/** * get_user_pages_fast() - pin user pages in memory
Quoted 7 lines · Lai Jiangshan
When page fault, we always call
get_user_pages(write=1).Actually, we don't need to do this when it is not write fault.
get_user_pages(write=1)will cause shared page(ksm) copied. If this page is not modified in future, this copying and the copied page are just wasted. Ksm may scan and merge them and may cause thrash.
But is page is written into afterwords we will get another page fault.
Quoted 3 lines
In this patch, if the page is RO for host VMM and it not write fault for guest, we will use RO page, otherwise we use a writable page.
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Quoted 39 lines
Signed-off-by Lai Jiangshan <[email protected]>---
arch/x86/kvm/mmu.c +29
-0@@ -1832,6 +1832,45 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) } } +/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + }
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 10 lines
arch/x86/kvm/mmu.c +10
-0+ + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ; +return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn() returns fault_page if page is mapped RO, so caller of kvm_get_pfn_for_page_fault() and kvm_get_pfn_for_gfn() will get different results when called on the same page. Not good. kvm_get_pfn_for_page_fault() logic should be folded into kvm_get_pfn_for_gfn().
Quoted 110 lines
arch/x86/kvm/mmu.c +9
-5static int mmu_need_write_protect (struct kvm_vcpu *vcpu,gfn_t gfn,bool can_unsync) {@@ -2085,6 +2124,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)int level;pfn_t pfn;unsigned long mmu_seq; +int host_writable; level =mapping_level (vcpu, gfn);@@ -2099,7 +2139,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, gfn, write, &host_writable);/* mmio */ if (is_error_pfn (pfn))@@ -2109,7 +2149,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)if (mmu_notifier_retry (vcpu, mmu_seq)) goto out_unlock;kvm_mmu_free_some_pages (vcpu); - r =__direct_map (vcpu, v, write, level, gfn, pfn, true); + r =__direct_map (vcpu, v, write, level, gfn, pfn, host_writable);spin_unlock (&vcpu->kvm->mmu_lock);@@ -2307,6 +2347,8 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,int level;gfn_t gfn = gpa >>PAGE_SHIFT ;unsigned long mmu_seq; +int write_fault = error_code &PFERR_WRITE_MASK ; +int host_writable;ASSERT (vcpu);ASSERT (VALID_PAGE (vcpu->arch.mmu.root_hpa));@@ -2321,15 +2363,16 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, gfn, write_fault, + &host_writable);if (is_error_pfn (pfn))return kvm_handle_bad_page (vcpu->kvm, gfn, pfn);spin_lock (&vcpu->kvm->mmu_lock);if (mmu_notifier_retry (vcpu, mmu_seq)) goto out_unlock;kvm_mmu_free_some_pages (vcpu); - r =__direct_map (vcpu, gpa, error_code &PFERR_WRITE_MASK , - level, gfn, pfn, true); + r =__direct_map (vcpu, gpa, write_fault, + level, gfn, pfn, host_writable);spin_unlock (&vcpu->kvm->mmu_lock);return r;arch/x86/kvm/paging_tmpl.h +4
-2@@ -430,6 +430,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,pfn_t pfn;int level =PT_PAGE_TABLE_LEVEL ;unsigned long mmu_seq; +int host_writable;pgprintk ("%s: addr %lx err %x\n" , __func__, addr, error_code);kvm_mmu_audit (vcpu,"pre page fault" );@@ -461,7 +462,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, mmu_seq = vcpu->kvm->mmu_notifier_seq;smp_rmb (); - pfn =kvm_get_pfn_for_gfn (vcpu->kvm, walker.gfn); + pfn =kvm_get_pfn_for_page_fault (vcpu->kvm, walker.gfn, write_fault, + &host_writable);/* mmio */ if (is_error_pfn (pfn))@@ -472,7 +474,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, goto out_unlock;kvm_mmu_free_some_pages (vcpu); sptep =FNAME (fetch)(vcpu, addr, &walker, user_fault, write_fault, - level, &write_pt, pfn, true); + level, &write_pt, pfn, host_writable); (void )sptep;pgprintk ("%s: shadow pte %p %llx ptwrite %d\n" , __func__, sptep, *sptep, write_pt);arch/x86/mm/gup.c +2
-1@@ -8,6 +8,7 @@#include <linux/mm.h> #include <linux/vmstat.h> #include <linux/highmem.h> +#include <linux/module.h> #include <asm/pgtable.h> @@ -274,6 +275,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,return nr; } +EXPORT_SYMBOL_GPL (__get_user_pages_fast);/** * get_user_pages_fast() - pin user pages in memory --
Quoted 3 lines
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Gleb.
Quoted 8 lines · Gleb Natapov
Quoted 7 lines · Lai Jiangshan
When page fault, we always call
get_user_pages(write=1).Actually, we don't need to do this when it is not write fault.
get_user_pages(write=1)will cause shared page(ksm) copied. If this page is not modified in future, this copying and the copied page are just wasted. Ksm may scan and merge them and may cause thrash.
Quoted 7 lines
But is page is written into afterwords we will get another page fault.
Quoted 3 lines
In this patch, if the page is RO for host VMM and it not write fault for guest, we will use RO page, otherwise we use a writable page.
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Except KSM pages.
Quoted 46 lines
Quoted 46 lines
Signed-off-by Lai Jiangshan <[email protected]>---
arch/x86/kvm/mmu.c +36
-0@@ -1832,6 +1832,45 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) } } +/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ;
Quoted 8 lines
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 3 lines
arch/x86/kvm/mmu.c +3
-0+return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn()returnsfault_pageif page is mapped RO, so caller ofkvm_get_pfn_for_page_fault()andkvm_get_pfn_for_gfn()will get different results when called on the same page. Not good.kvm_get_pfn_for_page_fault()logic should be folded intokvm_get_pfn_for_gfn().
Agreed. Please keep gfn_to_pfn related code in virt/kvm/kvm_main.c.
Quoted 11 lines · Marcelo Tosatti
Quoted 10 lines · Gleb Natapov
Quoted 7 lines · Lai Jiangshan
When page fault, we always call
get_user_pages(write=1).Actually, we don't need to do this when it is not write fault.
get_user_pages(write=1)will cause shared page(ksm) copied. If this page is not modified in future, this copying and the copied page are just wasted. Ksm may scan and merge them and may cause thrash.But is page is written into afterwords we will get another page fault.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
In this patch, if the page is RO for host VMM and it not write fault for guest, we will use RO page, otherwise we use a writable page.
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Except KSM pages.
KSM page will be COWed by __get_user_pages_fast(addr, 1, 1, page) in get_user_page_and_protection() just like it COWed now, no?
Quoted 55 lines
Quoted 46 lines
Quoted 46 lines
Signed-off-by Lai Jiangshan <[email protected]>---
arch/x86/kvm/mmu.c +36
-0@@ -1832,6 +1832,45 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) } } +/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ;Quoted 8 lines
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 3 lines
arch/x86/kvm/mmu.c +3
-0+return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn()returnsfault_pageif page is mapped RO, so caller ofkvm_get_pfn_for_page_fault()andkvm_get_pfn_for_gfn()will get different results when called on the same page. Not good.kvm_get_pfn_for_page_fault()logic should be folded intokvm_get_pfn_for_gfn().
Quoted 1 line
Agreed. Please keep
gfn_to_pfnrelated code invirt/kvm/kvm_main.c.
--
Gleb.
Quoted 10 lines · Gleb Natapov
Quoted 7 lines
Quoted 4 lines
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Except KSM pages.
KSM page will be COWed by
__get_user_pages_fast(addr, 1, 1, page)inget_user_page_and_protection()just like it COWed now, no?
Well, we don't want to COW it on write faults.
The optimal behaviour is:
- write faults: COW and instantiate a writeable spte
- read faults: instantiate a readable spte; if likely(page is writeable), make it a writeable spte; if likely(page is dirty) make it a dirty spte
- speculative spte instantiations: if likely(page is present) instantiate a pte; if accessed, mark it accessed, if writeable, mark it writeable; if dirty, mark it dirty
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 6 lines · Avi Kivity
Quoted 5 lines · Gleb Natapov
Quoted 5 lines
Quoted 3 lines
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Except KSM pages.
Quoted 8 lines
Quoted 2 lines
KSM page will be COWed by
__get_user_pages_fast(addr, 1, 1, page)inget_user_page_and_protection()just like it COWed now, no?Well, we don't want to COW it on write faults.
The optimal behaviour is:
- write faults: COW and instantiate a writeable spte
So do we or don't we want to COW on write faults?
--
Gleb.
Quoted 7 lines · Gleb Natapov
Quoted 5 lines · Avi Kivity
Quoted 3 lines · Gleb Natapov
Quoted 3 lines
Quoted 3 lines
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Except KSM pages.
KSM page will be COWed by
__get_user_pages_fast(addr, 1, 1, page)inget_user_page_and_protection()just like it COWed now, no?Well, we don't want to COW it on write faults.
I meant read faults here.
Quoted 6 lines
Quoted 4 lines
The optimal behaviour is:
- write faults: COW and instantiate a writeable spte
So do we or don't we want to COW on write faults?
We do (no choice).
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 74 lines · Marcelo Tosatti
Quoted 8 lines · Gleb Natapov
Quoted 7 lines · Lai Jiangshan
When page fault, we always call
get_user_pages(write=1).Actually, we don't need to do this when it is not write fault.
get_user_pages(write=1)will cause shared page(ksm) copied. If this page is not modified in future, this copying and the copied page are just wasted. Ksm may scan and merge them and may cause thrash.Quoted 7 lines
But is page is written into afterwords we will get another page fault.
Quoted 3 lines
In this patch, if the page is RO for host VMM and it not write fault for guest, we will use RO page, otherwise we use a writable page.
Currently pages allocated for guest memory are required to be RW, so after your series behaviour will remain exactly the same as before.
Except KSM pages.
Quoted 46 lines
Quoted 46 lines
Signed-off-by Lai Jiangshan <[email protected]>---
arch/x86/kvm/mmu.c +36
-0@@ -1832,6 +1832,45 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) } } +/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ;Quoted 8 lines
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 3 lines
arch/x86/kvm/mmu.c +3
-0+return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn()returnsfault_pageif page is mapped RO, so caller ofkvm_get_pfn_for_page_fault()andkvm_get_pfn_for_gfn()will get different results when called on the same page. Not good.kvm_get_pfn_for_page_fault()logic should be folded intokvm_get_pfn_for_gfn().
Quoted 3 lines
Agreed. Please keep
gfn_to_pfnrelated code invirt/kvm/kvm_main.c.
Pass write_fault parameter to kvm_get_pfn_for_gfn()? But only X86 use this parameter currently, I think it is OK to keep these code in arch/x86/kvm/mmu.c
Quoted 36 lines · Gleb Natapov
Quoted 36 lines
arch/x86/kvm/mmu.c +36
-0+/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ;
Quoted 9 lines
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 3 lines
arch/x86/kvm/mmu.c +3
-0+return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn()returnsfault_pageif page is mapped RO, so caller ofkvm_get_pfn_for_page_fault()andkvm_get_pfn_for_gfn()will get different results when called on the same page. Not good.kvm_get_pfn_for_page_fault()logic should be folded intokvm_get_pfn_for_gfn().
The different results are the things we just need. We don't want to copy and write a page which is mapped RO when only read fault.
Quoted 49 lines · Lai Jiangshan
Quoted 36 lines · Gleb Natapov
Quoted 36 lines
arch/x86/kvm/mmu.c +36
-0+/* get a current mapped page fast, and test whether the page is writable. */ +static struct page *get_user_page_and_protection (unsigned long addr, +int *writable) +{ +struct page *page[1 ]; + +if (__get_user_pages_fast (addr,1 ,1 , page) ==1 ) { + *writable =1 ; +return page[0 ]; + } +if (__get_user_pages_fast (addr,1 ,0 , page) ==1 ) { + *writable =0 ; +return page[0 ]; + } +return NULL ; +} + +static pfn_t kvm_get_pfn_for_page_fault (struct kvm *kvm,gfn_t gfn, +int write_fault,int *host_writable) +{ +unsigned long addr; +struct page *page; + +if (!write_fault) { + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr)) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + page =get_user_page_and_protection (addr, host_writable); +if (page) +return page_to_pfn (page); + } + + *host_writable =1 ;Quoted 9 lines
arch/x86/kvm/mmu.c static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn) · line 1873
Quoted 3 lines
arch/x86/kvm/mmu.c +3
-0+return kvm_get_pfn_for_gfn (kvm, gfn); +} +
kvm_get_pfn_for_gfn()returnsfault_pageif page is mapped RO, so caller ofkvm_get_pfn_for_page_fault()andkvm_get_pfn_for_gfn()will get different results when called on the same page. Not good.kvm_get_pfn_for_page_fault()logic should be folded intokvm_get_pfn_for_gfn().
Quoted 1 line
The different results are the things we just need.
How so? Users of kvm_get_pfn_for_gfn() will think that page is invalid and may report misconfiguration to userspace and users of kvm_get_pfn_for_page_fault() will think that the access to page is OK. There are no many users of kvm_get_pfn_for_gfn() and may be your patch replace all of them with kvm_get_pfn_for_page_fault(), but this just strengthen the point that they should be merged.
Quoted 2 lines
We don't want to copy and write a page which is mapped RO when only read fault.
I don't see how returning inconsistent results helps us achieving that.
BTW since kvm_get_pfn_for_gfn() will never map RO page get_user_page_and_protection() will never find any RO pages. Looks like kvm_get_pfn_for_page_fault() is equivalent to kvm_get_pfn_for_gfn() since !write_fault section will at best find mapped RW page.
--
Gleb.