KVM: s390: vsie: use common code functions for pinning
PATCH v2 1/1 KVM: s390: vsie: use common code functions for pinning
We will not see -ENOMEM (gfn_to_hva() will return KVM_ERR_PTR_BAD_PAGE for all errors). So we can also get rid of special handling in the callers of pin_guest_page() and always assume that it is a g2 error.
As also kvm_s390_inject_program_int() should never fail, we can simplify pin_scb(), too.
---
Based on:git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git next
v1- > v2:
- As Christian noticed, gfn_to_page() will never return -ENOMEM. So we can simplify pin_guest_page() even more and also the callers.
arch/s390/kvm/vsie.c | 50 +++++++++++++++++------------------------------- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 4 ++-- 3 files changed, 21 insertions(+), 34 deletions(-)
arch/s390/kvm/vsie.c +18 -32
@@ -443,22 +443,14 @@ static int map_prefix(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)* * Returns: - 0 on success * - -EINVAL if the gpa is not valid guest storage -* - -ENOMEM if out of memory */ static int pin_guest_page (struct kvm *kvm,gpa_t gpa,hpa_t *hpa) {struct page *page; -hva_t hva; -int rc; - hva =gfn_to_hva (kvm,gpa_to_gfn (gpa)); -if (kvm_is_error_hva (hva)) + page =gfn_to_page (kvm,gpa_to_gfn (gpa)); +if (is_error_page (page))return -EINVAL ; - rc =get_user_pages_fast (hva,1 ,1 , &page); -if (rc <0 ) -return rc; -else if (rc !=1 ) -return -ENOMEM ; *hpa = (hpa_t )page_to_virt (page) + (gpa & ~PAGE_MASK );return 0 ; }
@@ -466,11 +458,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ static void unpin_guest_page (struct kvm *kvm,gpa_t gpa,hpa_t hpa) { -struct page *page; - - page =virt_to_page (hpa); -set_page_dirty_lock (page); -put_page (page); +kvm_release_pfn_dirty (hpa >>PAGE_SHIFT );/* mark the page always as dirty for migration */ mark_page_dirty (kvm,gpa_to_gfn (gpa)); }
@@ -557,7 +545,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) rc =set_validity_icpt (scb_s,0x003bU );if (!rc) { rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) +if (rc) rc =set_validity_icpt (scb_s,0x0034U ); }if (rc)
@@ -574,10 +562,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) }/* 256 bytes cannot cross page boundaries */ rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) +if (rc) { rc =set_validity_icpt (scb_s,0x0080U ); -if (rc) goto unpin; + } scb_s->itdba = hpa; }
@@ -592,10 +580,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)* if this block gets bigger, we have to shadow it. */ rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) +if (rc) { rc =set_validity_icpt (scb_s,0x1310U ); -if (rc) goto unpin; + } scb_s->gvrd = hpa; }
@@ -607,11 +595,11 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) }/* 64 bytes cannot cross page boundaries */ rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) +if (rc) { rc =set_validity_icpt (scb_s,0x0043U ); -/* Validity 0x0044 will be checked by SIE */ -if (rc) goto unpin; + } +/* Validity 0x0044 will be checked by SIE */ scb_s->riccbd = hpa; }if ((scb_s->ecb &ECB_GS ) && !(scb_s->ecd &ECD_HOSTREGMGMT )) {
@@ -635,10 +623,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)* cross page boundaries */ rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) +if (rc) { rc =set_validity_icpt (scb_s,0x10b0U ); -if (rc) goto unpin; + } scb_s->sdnxo = hpa | sdnxc; }return 0 ;
@@ -663,7 +651,6 @@ static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,* * Returns: - 0 if the scb was pinned. * - > 0 if control has to be given to guest 2 -* - -ENOMEM if out of memory */ static int pin_scb (struct kvm_vcpu *vcpu,struct vsie_page *vsie_page,gpa_t gpa)
@@ -672,14 +659,13 @@ static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,int rc; rc =pin_guest_page (vcpu->kvm, gpa, &hpa); -if (rc == -EINVAL ) { +if (rc) { rc =kvm_s390_inject_program_int (vcpu,PGM_ADDRESSING ); -if (!rc) - rc =1 ; +WARN_ON_ONCE (rc); +return 1 ; } -if (!rc) - vsie_page->scb_o = (struct kvm_s390_sie_block *) hpa; -return rc; + vsie_page->scb_o = (struct kvm_s390_sie_block *) hpa; +return 0 ; }/*
include/linux/kvm_host.h +1 -0
@@ -664,6 +664,7 @@ kvm_pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool *writable);void kvm_release_pfn_clean (kvm_pfn_t pfn); +void kvm_release_pfn_dirty (kvm_pfn_t pfn);void kvm_set_pfn_dirty (kvm_pfn_t pfn);void kvm_set_pfn_accessed (kvm_pfn_t pfn);void kvm_get_pfn (kvm_pfn_t pfn);
virt/kvm/kvm_main.c +2 -2
@@ -122,7 +122,6 @@ static void hardware_disable_all(void);static void kvm_io_bus_destroy (struct kvm_io_bus *bus); -static void kvm_release_pfn_dirty (kvm_pfn_t pfn);static void mark_page_dirty_in_slot (struct kvm_memory_slot *memslot,gfn_t gfn); __visible bool kvm_rebooting;
@@ -1723,11 +1722,12 @@ void kvm_release_page_dirty(struct page *page) }EXPORT_SYMBOL_GPL (kvm_release_page_dirty); -static void kvm_release_pfn_dirty (kvm_pfn_t pfn) +void kvm_release_pfn_dirty (kvm_pfn_t pfn) {kvm_set_pfn_dirty (pfn);kvm_release_pfn_clean (pfn); } +EXPORT_SYMBOL_GPL (kvm_release_pfn_dirty);void kvm_set_pfn_dirty (kvm_pfn_t pfn) {
Signature · 1 line
2.13.5
Quoted 13 lines · On Fri, 1 Sep 2017 17:11:43 +0200 David Hildenbrand <[email protected]> wrote:
We will not see
-ENOMEM(gfn_to_hva()will returnKVM_ERR_PTR_BAD_PAGEfor all errors). So we can also get rid of special handling in the callers ofpin_guest_page()and always assume that it is a g2 error.As also
kvm_s390_inject_program_int()should never fail, we can simplifypin_scb(), too.Signed-off-by David Hildenbrand <[email protected]>---
Based on:
git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.gitnext
Quoted 8 lines
v1- > v2:
- As Christian noticed,gfn_to_page()will never return-ENOMEM. So we can simplifypin_guest_page()even more and also the callers.arch/s390/kvm/vsie.c | 50 +++++++++++++++++------------------------------- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 4 ++-- 3 files changed, 21 insertions(+), 34 deletions(-)
arch/s390/kvm/vsie.c static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) · line 464
Quoted 13 lines · David Hildenbrand
arch/s390/kvm/vsie.c +1
-5@@ -466,11 +458,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ static void unpin_guest_page (struct kvm *kvm,gpa_t gpa,hpa_t hpa) { -struct page *page; - - page =virt_to_page (hpa); -set_page_dirty_lock (page); -put_page (page); +kvm_release_pfn_dirty (hpa >>PAGE_SHIFT );/* mark the page always as dirty for migration */ mark_page_dirty (kvm,gpa_to_gfn (gpa)); }
This is probably ok but can you maybe outline why we no longer need the _lock variant of set_page_dirty? (Or asked differently: why did we use the _locked varaint)
Other than that everything looks sane, I am just not sure about this part.
Quoted 22 lines · Christian Borntraeger
arch/s390/kvm/vsie.c static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) · line 464
Quoted 13 lines · David Hildenbrand
arch/s390/kvm/vsie.c +1
-5@@ -466,11 +458,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ static void unpin_guest_page (struct kvm *kvm,gpa_t gpa,hpa_t hpa) { -struct page *page; - - page =virt_to_page (hpa); -set_page_dirty_lock (page); -put_page (page); +kvm_release_pfn_dirty (hpa >>PAGE_SHIFT );/* mark the page always as dirty for migration */ mark_page_dirty (kvm,gpa_to_gfn (gpa)); }This is probably ok but can you maybe outline why we no longer need the
_lockvariant ofset_page_dirty? (Or asked differently: why did we use the_lockedvaraint)
Quoted 2 lines
Other than that everything looks sane, I am just not sure about this part.
Short answer: As x86 uses this heavily, I was expecting it to do the right thing. I can't sport any additional page locking in x86 code.
Long answer:
mm/page-writeback.c: It only seems to be relevant for !anonymous mappings.
"set_page_dirty() is racy [...] This is because another CPU could truncate the page off the mapping and then free the mapping."
"For pages with a mapping this should be done under the page lock for the benefit of asynchronous memory errors who prefer a consistent dirty state. This rule can be broken in some special cases [...]"
Sounds to me like the worst thing that could happen is losing a dirty flag. Which should not matter if somebody tries to do nasty stuff with the mapping either way.
But to verify: Paolo, can you recall why we don't need the _locked variants in kvm common code?
Signature · 3 lines
Thanks, David
Ping Paolo :)
Quoted 29 lines
Quoted 6 lines
This is probably ok but can you maybe outline why we no longer need the
_lockvariant ofset_page_dirty? (Or asked differently: why did we use the_lockedvaraint)Other than that everything looks sane, I am just not sure about this part.
Short answer: As x86 uses this heavily, I was expecting it to do the right thing. I can't sport any additional page locking in x86 code.
Long answer:
mm/page-writeback.c: It only seems to be relevant for !anonymous mappings."
set_page_dirty()is racy [...] This is because another CPU could truncate the page off the mapping and then free the mapping.""For pages with a mapping this should be done under the page lock for the benefit of asynchronous memory errors who prefer a consistent dirty state. This rule can be broken in some special cases [...]"
Sounds to me like the worst thing that could happen is losing a dirty flag. Which should not matter if somebody tries to do nasty stuff with the mapping either way.
But to verify: Paolo, can you recall why we don't need the
_lockedvariants in kvm common code?
Signature · 3 lines
Thanks, David
Paolo Bonzini
maintainerQuoted 9 lines · David Hildenbrand
mm/page-writeback.c: It only seems to be relevant for !anonymous mappings."
set_page_dirty()is racy [...] This is because another CPU could truncate the page off the mapping and then free the mapping.""For pages with a mapping this should be done under the page lock for the benefit of asynchronous memory errors who prefer a consistent dirty state. This rule can be broken in some special cases [...]"
Quoted 7 lines
Sounds to me like the worst thing that could happen is losing a dirty flag. Which should not matter if somebody tries to do nasty stuff with the mapping either way.
But to verify: Paolo, can you recall why we don't need the
_lockedvariants in kvm common code?
That predates me by several years, but if you look at set_page_dirty it is
if (page_mapping(page)) return ...->set_page_dirty(page); else return TestSetPageDirty(page);
so KVM basically expects that !page_mapping(page) or that the page is ram-backed. That includes a hugetlbfs page, for which the callback is *also* doing just SetPageDirty, or a few non-anonymous maps for which the callback is __set_page_dirty_no_writeback; these in turn are ramfs and tmpfs.
If you map files to KVM, the page cache (fs/buffer.c) is not going to know that the pages are dirty, and likewise the file system is not going to know that the inode is dirty. So don't do that.
Paolo
Christian Borntraeger
AppliedQuoted 8 lines · David Hildenbrand
We will not see
-ENOMEM(gfn_to_hva()will returnKVM_ERR_PTR_BAD_PAGEfor all errors). So we can also get rid of special handling in the callers ofpin_guest_page()and always assume that it is a g2 error.As also
kvm_s390_inject_program_int()should never fail, we can simplifypin_scb(), too.Signed-off-by David Hildenbrand <[email protected]>
Thanks, applied.